Minitestで例外クラスとエラーメッセージを検証する方法
Minitestで例外クラスと例外メッセージを検証する場合は、assert_raises
でまず例外クラスを検証し、その戻り値(例外オブジェクト)を使ってエラーメッセージを検証します。
以下はそのサンプルコードです。
次のようなクラスがあったとします。
class Foo
# 例外が発生するメソッド
def self.boo!
raise 'BOO!!'
end
end
boo!
メソッドを呼び出すとRuntimeErrorが発生し、なおかつそのエラーメッセージが"BOO!!"であることを検証する場合は次のようなコードを書きます。
require 'minitest/autorun'
class FooTest < Minitest::Test
def test_boo!
# RuntimeErrorが発生することを検証
e = assert_raises RuntimeError do
Foo.boo!
end
# エラーメッセージを検証
assert_equal 'BOO!!', e.message
end
end
よくある間違い
assert_raises
の第2引数に文字列を渡してもエラーメッセージを検証したことにはなりません。
以下のコードは間違いです。
require 'minitest/autorun'
class FooTest < Minitest::Test
def test_boo!
# このコードだと、エラーメッセージを検証できていない
assert_raises RuntimeError, 'BOO!!' do
Foo.boo!
end
end
end
assert_raises
の第2引数は、検証が失敗したときに表示されるメッセージになります。
assert_raises
自体は例外クラスが一致するかどうかの検証しかしてくれません。
たとえば、次のように実際のエラーメッセージと異なる文字列を渡してもテストはパスします。
require 'minitest/autorun'
class FooTest < Minitest::Test
def test_boo!
# 第2引数は検証失敗時の表示用メッセージなので、'BOO!!'でなくてもパスする
assert_raises RuntimeError, 'HAHAHA!!' do
Foo.boo!
end
end
end
これは「パスすべきでないテストがパスする問題(false positive)」につながるので要注意です。
同様に、次のコードも間違いです。
require 'minitest/autorun'
class FooTest < Minitest::Test
def test_boo!
# これもやはりエラーメッセージを検証できていない
assert_raises 'BOO!!' do
Foo.boo!
end
end
end
例外クラスが省略された場合は、StandardErrorか、そのサブクラスの例外が発生した場合にパスします。
上のコードの'BOO!!'
は例外クラスではなく文字列なので、先ほどと同様、検証失敗時に表示されるメッセージになります。
さらに:コードを読んで仕様を理解する
Minitestの実装コードはシンプルなので、assert_raises
メソッドのコードを読むと仕様の理解がさらに進むと思います。
参考:RSpecの場合
RSpecであれば、raise_error
マッチャを使って例外クラスとエラーメッセージを同時に検証できます。
# 例外クラスとエラーメッセージを同時に検証する
expect { Foo.boo! }.to raise_error RuntimeError, 'BOO!!'
文字列だけを渡したときはエラーメッセージだけを検証したことになります。
# エラーメッセージだけを検証する
expect { Foo.boo! }.to raise_error 'BOO!!'
次のようにブロックを使うと、例外オブジェクトを使って検証することもできます。
expect { Foo.boo! }.to raise_error RuntimeError do |e|
expect(e.message).to eq 'BOO!!'
end
ぱっと見、raise_error
マッチャとMinitestのassert_raises
メソッドと用途や構文が似ているので、同じように使えそうな気がしますが、実際は微妙に異なるので注意が必要です。