勉強会/Railsによるテスト作成 http://www.ark-web.jp/sandbox/wiki/91.html

[edit]

目次

[edit]

Railsによるテスト作成

http://www.pragmaticprogrammer.com/title/rails/
の12章「Task T: Testing」と
http://jp.rubyist.net/magazine/?0013-RubyOnRails
を参照しながらRailsによるユニットテスト、機能テスト、自動化テストの作成を行ってみる

[edit]

準備

スケルトンを作る

# rails gentest

この段階でgentest/test/にはユニットテスト、機能テストのスケルトンも作成されている

# cd gentest/test

# dir

fixtures/
functional/
mocks/
unit/
test_helper.rb

fixturesはテスト用データベースの初期化時に初期データとして投入するデータを定義したYAML(*.yml)ファイルを格納するディレクトリ、functionalは機能テストを格納するディレクトリ、mocksはモックを格納するディレクトリ、unitはユニットテストを格納するディレクトリになる。test_helper.rbはテスト用の共通関数、定義置き場。

[edit]

モデル定義とテスト作成

モデルを定義して、テストを作成する。

[edit]

準備

DBテーブルは下記を使う(テーブル名、カラム名はRailsの規約に従う)

-- books
create table books (
  id                    integer primary key,
  title                 varchar(255) not null,
  description           text,
  updated_at            datetime
);

このテーブルスキーマをdb/schema.sqlとして保存し、下記でデータベースを作成する(SQLiteを使用)

# cd db

# sqlite3 gentest.db < schema.sql

接続情報をconfig/database.ymlに記載する

development:
  adapter: sqlite3
  dbfile: db/gentest.db

モデルクラス(Book)のスケルトンを作成する

# ruby script/generate model Book

この段階で、
test/unit/book_test.rb
test/fixtures/books.yml
が自動作成される

book_test.rbはBookクラス用のユニットテスト、books.ymlはBookモデル用のfixtureになる。

[edit]

最初のテスト実行

test/unit/book_test.rbは下記のようになっている

require File.dirname(__FILE__) + '/../test_helper'

class BookTest < Test::Unit::TestCase
  fixtures :books

  # Replace this with your real tests.
  def test_truth
    assert_kind_of Book, books(:first)
  end
end

RailsのテストクラスはTest::Unit::TestCaseクラスのサブクラスになっている。Test::UnitフレームワークはRailsにバンドルされているので、この段階ですぐにテスト実行してみることができる。

# ruby test/unit/book_test.rb

Railsはtestスクリプトを実行するたびに、テストデータベースを作成し、テストメソッドを実行するたびにfixtureのデータでテストデータベースのデータを初期化する。
しかし、まだテストデータべースにdb/gentest.dbのスキーマを定義していないので、この検査は失敗する。

テストデータベースのスキーマ定義は下記のrakeコマンドによって実行する

# rake clone_structure_to_test

これで、スキーマだけがテストデータベースに定義される・・・はずなのだが、自分の環境では下記のエラーを吐いて失敗する

rake aborted!
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occured while evaluating nil.[]

ちょっと調べてみたが、原因はまだ不明。
ので、以下は実際に動作確認なしの情報になる

[edit]

fixture定義

スケルトン状態ではtest/fixtures/books.ymlの内容は下記の通り

# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
first:
  id: 1
another:
  id: 2

idだけがデータセットされている状態。

fixtureとして下記を記述する

first:
  id: 1
  title: 基礎からはじめるRuby
  description: 基礎からはじめるRubyの説明
second:
  id: 2
  title: Effective Java
  desctiption: 〜〜〜〜〜〜〜
[edit]

モデルオブジェクト作成とレコード読み込みのテスト

test/unit/book_test.rbにsetup()、test_create()メソッドを下記のように定義する

(Test::Unitはtest_で始まるメソッドを全て自動実行する。またsetup()メソッドは各テストメソッドが呼ばれる前に必ず実行される)

 def setup
	@book = Book.find(1)
 end
 
 def test_create
	assert_kind_of Book, @book
	assert_equal 1, @book.id
	assert_equal "基礎からはじめるRuby", @book.title
	assert_equal "基礎からはじめるRubyの説明", @book.description
 end

setup()メソッドではidが1のBookレコードを@bookに格納し、各テストメソッドで参照できるようにしている。

test_create()メソッドではまず、@bookがBookモデルのオブジェクトであることを確認し、その後オブジェクトのid、title、descriptonが正しくセットされていることを確認している。

[edit]

レコード更新のテスト

test/unit/book_test.rbにtest_update()メソッドを下記のように定義する。

 def test_update
	assert_equal "基礎からはじめるRuby", @book.title
	@book.title = "新・基礎からはじめるRuby";
	assert @book.save @book.errors.full_messages.join("; ")
	@book.reload
	assert_equal "新・基礎からはじめるRuby", @book.title
 end

assertメソッドでテストデータベースへの更新が正しく行われることをチェックし、その後reload()メソッドで@bookの状態を更新したら、正しく更新されたことを確認している

[edit]

レコード削除のテスト

test/unit/book_test.rbにtest_destroy()メソッドを下記のように定義する。

 def test_destroy
	@book.destroy
	assert_raise(ActiveRecord::RecordNotFound) { Book.find(@book.id) }
 end

レコードを削除した後、そのレコードを検索するとActiveRecord::RecordNotFoundのExceptionが発生することをassert_raiseメソッドでチェックしている

[edit]

DRYの導入

books.ymlに定義した値(基礎からはじめるRuby等)をテストコード中にまた記述してしまうのはDRY(Don't Repeat Yourself)に反する。
フレキシビリティを保ちつつ、テストコードを書くには下記のようにする

 def test_create
	assert_kind_of Book, @book
	# fixture名を指定して取り出すことができる(@bookではなく@booksであることに注意)
	first_book = @books["first"]
	assert_equal first_book["id"], @book.id
	assert_equal first_book["title"], @book.title
	assert_equal first_book["description"], @book.description
 end

下記のようにすれば、マッチするfixtureを自動で探して値をとってきてくれるのでより楽。(@fixture名_カラム名)

 def test_create
	assert_kind_of Book, @book
	assert_equal @first_id, @book.id
	assert_equal @first_title, @book.title
	assert_equal @first_description, @book.description
 end

投稿者進地 | パーマリンク

| append.gif

tag: Ruby on Rails


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2007-07-26 (木) 09:44:36 (4407d)

アークウェブのサービスやソリューションはこちら