2007年12月3日月曜日

Ruby Test::Unit

品質向上のために、Ruby のテスト技法を勉強しています。
理解を深めるためにブログを書くメソッド。

以下では、Ruby のユニットテストを行うクラス Test::Unit を利用したテスト技法について説明します。

テストされるクラスを設定

簡単なテストの例を見てみます。まず、テスト対象のクラスを設定します。
class Foo
def foo
return 'foo'
end

def bar
return 'bar'
end
end

テストコードを書く

次に上記のクラスのメソッドをテストするクラスを以下のように書きます。
require 'test/unit'
class FooTest < Test::Unit::TestCase
# 各テストメソッドが呼ばれる前に呼ばれるメソッド
def setup
puts '新しいテストを開始します。'
@obj = Foo.new
end

# 各テストメソッドが呼ばれた後に呼ばれるメソッド
def teardown
puts 'テストを 1 つ終了しました。'
end

def test_foo
# 成功するテストケース
# @obj.foo の値が foo となっているかどうかテスト
assert_equal('foo', @obj.foo)

# 上記は以下のようにも書ける。
# 第一引数が false のとき、第二引数のコメントが出力される
assert 'foo' == @obj.foo, '@obj.foo は foo のはず'
end

def test_bar
# 失敗するテストケース
# @obj.bar の値が foo となっているかテスト
# 第一引数のテストが失敗するので第二引数が出力される
assert 'foo' == @obj.bar, '@obj.bar は foo のはず'
end
end

テストコードの解説

上記のテストコードを 1 つ 1 つ解説していきます。
まず、 ユニットテストプログラムを書き、実行するのにクラスや必要なクラスや機能を以下の文で読み込みます。
require 'test/unit'
次に Foo クラスのメソッドをテストするクラス FooTest クラスを、 Test::Unit::TestCase クラスを継承して宣言します。
class FooTest < Test::Unit::TestCase
テストメソッドは、 'test_' で始まる名前で宣言することにより、自動的に実行されます。
def test_foo
....
end
最後に次の文によりテストを実行します。
assert_equal('foo', @obj.foo)
assert メソッドは、Test::Unit::Assertions モジュールのメソッドです。
定義したクラスのメソッドが期待した通りに動くかを第一引数で、失敗したときのメッセージを第二引数で指定します。

assert メソッドは用途に応じて色々ありますので、以下を参考にしてください。
Ruby リファレンスマニュアル Test::Unit

テスト実行

定義した Foo クラスの各メソッドが、期待した通りに動くか、ユニットテストを行うクラス FooTest クラスを作成し、テストしてみましょう。
上記のテストケースを書いたスクリプトを initial_test.rb として保存して、コマンドラインから実行してみます。
# ruby initial_test.rb
実行結果は以下のようになります。
Loaded suite initial
Started
新しいテストを開始します。
Fテストを 1 つ終了しました。
新しいテストを開始します。
テストを 1 つ終了しました。
.
Finished in 0.011519 seconds.

1) Failure:
test_bar(FooTest) [initial.rb:41]:
@obj.bar は foo のはず.
<false> is not true.

2 tests, 3 assertions, 1 failures, 0 errors</false>
2 テスト実行、3 回 assert メソッドの呼出し、1 回失敗、という結果になっています。
このように、テストに失敗した場合、失敗のメッセージが出力されますので Foo クラス、またはテストコードを修正しテストが通る状態を目指します。

主要プレイヤー

上記の例を通し、テストの主要 4 プレイヤーの存在が見えてきます。
  • Assertion
    • オブジェクトが期待する結果か評価する 1 行のコード。
  • Test
    • assert メソッドを含むメソッド。上の例なら、test_foo など。
  • Test Case
    • Test::Unit::TestCase を継承した Test メソッドを持つクラス
  • Test Suite
    • Test Case の集合。複数のテストを実行するのに、各々のテストを個々に動かさずとも、各々のテストを含んだ TestSuite を実行することもできる。
他: 参考サイト
A Guide to Testing the Rails
プログラミング wiki (Test::Unit) チュートリアル