はじめに
minitest内で例外を任意で発生させる方法について整理してまとめます。
minitest内で例外を発生する主な方法の比較表
方法 | コード例 | 副作用の有無 | 特徴 |
---|---|---|---|
① stub | obj.stub(:fail, -> {raise "RuntimeError"}) {...} |
なし(ブロック内のみ有効) | 一時的にメソッドを差し替えられる。元のオブジェクトを汚さない |
② 特異メソッド | def obj.fail; raise "RuntimeError"; end |
あり(同じインスタンスを再利用すると残る) | 直感的に書ける |
③ define_method | klass = Class.new {define_method(:fail) {raise "RuntimeError"}} |
あり(クラスを共有すると残る) | 複数メソッドをまとめて定義できる |
④ Minitest::Mock | mock = Minitest::Mock.new; def mock.fail; raise "RuntimeError"; end; mock.expect(:fail, nil); ... |
なし(テストごとに新規作成) |
expect メソッドと組み合わせる。呼び出し回数や引数の検証も可能 |
⑤ Proc / lambda | assert_raises(RuntimeError, &-> {raise "RuntimeError"}) |
なし | 最小限のコードで例外テスト可能 |
① stub
obj = Object.new
def obj.fail; end
obj.stub(:fail, -> {raise "RuntimeError"}) do
assert_raises(RuntimeError){obj.fail}
end
-
stub
で一時的に例外を仕込む- ObjectクラスはRubyの全てのクラスの祖先(BasicObjectを除く)。何の振る舞いも持たない、テスト用のダミーオブジェクトを作成する際に重宝される
- ブロックの中だけで差し替わるので副作用が残らない
② 特異メソッド
obj = Object.new
def obj.fail
raise "RuntimeError"
end
assert_raises(RuntimeError){obj.fail}
- オブジェクトに直接メソッドを定義する
- 同じインスタンスを使い回すと副作用が残る
③ define_method
klass = Class.new do
define_method(:fail) do
raise "RuntimeError"
end
end
obj = klass.new
assert_raises(RuntimeError) {obj.fail}
- クラス定義の中でメソッドを定義する
- 複数メソッドを定義できる
- クラスを共有する他のテストに影響する可能性がある
④ Minitest::Mock
mock = Minitest::Mock.new
def mock.fail
raise "RuntimeError"
end
mock.expect(:fail, nil)
assert_raises(RuntimeError) {mock.fail}
mock.verify
- Minitest::Mockのオブジェクトに特異メソッドを定義して例外を発生させる
-
verify
を呼び出すことでexpect
の検証を行う
⑤ Proc/lambdaを呼び出す
raise_proc = -> {raise "RuntimeError"}
assert_raises(RuntimeError, &raise_proc)
- オブジェクトは作らない
- メソッドの呼び出しの形にはならない