yield マッチャー
メソッドが yield するかどうか、何回 yield するか、引数を伴って yield するか、およびその引数が何であるかを指定するための関連する4つのマッチャーがあります。
yield_controlは、テスト対象のメソッドがyieldする場合にマッチします。引数がyieldされるかどうかは関係ありません。yield_with_argsは、テスト対象のメソッドが引数を伴ってyieldする場合にマッチします。このマッチャーに引数が指定されている場合、実際の引数が期待される引数と一致する場合にのみパスします。一致の判定には===または==が使用されます。yield_with_no_argsは、テスト対象のメソッドが引数なしでyieldする場合にマッチします。yield_successive_argsは、イテレータ向けに設計されており、テスト対象のメソッドがこのマッチャーに渡された引数の数と同じ回数だけyieldし、実際の引数が期待される引数と一致する場合にマッチします。一致の判定には===または==が使用されます。
注意: あなたの expect ブロックは、ブロックとしてメソッドに渡される引数を受け入れる必要があります。これは、マッチャーがメ ソッドが yield するかどうか、そして yield する場合は何回、どのような引数が yield されるかを検出するための「プローブ」として機能します。
バックグラウンド
次の内容で "my_class.rb" という名前のファイルがあるとします:
class MyClass
def self.yield_once_with(*args)
yield *args
end
def self.yield_twice_with(*args)
2.times { yield *args }
end
def self.raw_yield
yield
end
def self.dont_yield
end
end
yield_control マッチャー
次の内容で "yield_control_spec.rb" という名前のファイルがあるとします:
require './my_class'
RSpec.describe "yield_control matcher" do
specify { expect { |b| MyClass.yield_once_with(1, &b) }.to yield_control }
specify { expect { |b| MyClass.dont_yield(&b) }.not_to yield_control }
specify { expect { |b| MyClass.yield_twice_with(1, &b) }.to yield_control.twice }
specify { expect { |b| MyClass.yield_twice_with(1, &b) }.to yield_control.exactly(2).times }
specify { expect { |b| MyClass.yield_twice_with(1, &b) }.to yield_control.at_least(1) }
specify { expect { |b| MyClass.yield_twice_with(1, &b) }.to yield_control.at_most(3).times }
# deliberate failures
specify { expect { |b| MyClass.yield_once_with(1, &b) }.not_to yield_control }
specify { expect { |b| MyClass.dont_yield(&b) }.to yield_control }
specify { expect { |b| MyClass.yield_once_with(1, &b) }.to yield_control.at_least(2).times }
specify { expect { |b| MyClass.yield_twice_with(1, &b) }.not_to yield_control.twice }
specify { expect { |b| MyClass.yield_twice_with(1, &b) }.not_to yield_control.at_least(2).times }
specify { expect { |b| MyClass.yield_twice_with(1, &b) }.not_to yield_control.at_least(1) }
specify { expect { |b| MyClass.yield_twice_with(1, &b) }.not_to yield_control.at_most(3).times }
end
rspec yield_control_spec.rb を実行すると、
出力には以下のすべてが含まれているはずです:
| 13 の例、7 の失敗 |
| 与えられたブロックがコントロールを yield することを期待しました |
| 与えられたブロックがコントロールを yield しないことを期待しました |
| 与えられたブロックが少なくとも2回コントロールを yield しないことを期待しました |
| 与えられたブロックが最大3回コントロールを yield しないことを期待しました |
yield_with_args マッチャー
次の内容で "yield_with_args_spec.rb" という名前のファイルがあるとします:
require './my_class'
RSpec.describe "yield_with_args matcher" do
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.to yield_with_args }
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.to yield_with_args("foo") }
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.to yield_with_args(String) }
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.to yield_with_args(/oo/) }
specify { expect { |b| MyClass.yield_once_with("foo", "bar", &b) }.to yield_with_args("foo", "bar") }
specify { expect { |b| MyClass.yield_once_with("foo", "bar", &b) }.to yield_with_args(String, String) }
specify { expect { |b| MyClass.yield_once_with("foo", "bar", &b) }.to yield_with_args(/fo/, /ar/) }
specify { expect { |b| MyClass.yield_once_with("foo", "bar", &b) }.not_to yield_with_args(17, "baz") }
# deliberate failures
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.not_to yield_with_args }
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.not_to yield_with_args("foo") }
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.not_to yield_with_args(String) }
specify { expect { |b| MyClass.yield_once_with("foo", &b) }.not_to yield_with_args(/oo/) }
specify { expect { |b| MyClass.yield_once_with("foo", "bar", &b) }.not_to yield_with_args("foo", "bar") }
specify { expect { |b| MyClass.yield_once_with("foo", "bar", &b) }.to yield_with_args(17, "baz") }
end
rspec yield_with_args_spec.rb を実行すると、
出力には以下のすべてが含まれているはずです:
| 14 の 例、6 の失敗 |
| 与えられたブロックが引数を伴って yield しないことを期待しました |
| 与えられたブロックが引数を伴って yield しないことを期待しましたが、実際には yield されました |
| 与えられたブロックが引数を伴って yield することを期待しましたが、実際には予期しない引数で yield されました |
yield_with_no_args マッチャー
Given ファイル名が "yield_with_no_args_spec.rb" のファイルがあるとき:
require './my_class'
RSpec.describe "yield_with_no_args matcher" do
specify { expect { |b| MyClass.raw_yield(&b) }.to yield_with_no_args }
specify { expect { |b| MyClass.dont_yield(&b) }.not_to yield_with_no_args }
specify { expect { |b| MyClass.yield_once_with("a", &b) }.not_to yield_with_no_args }
# deliberate failures
specify { expect { |b| MyClass.raw_yield(&b) }.not_to yield_with_no_args }
specify { expect { |b| MyClass.dont_yield(&b) }.to yield_with_no_args }
specify { expect { |b| MyClass.yield_once_with("a", &b) }.to yield_with_no_args }
end
When rspec yield_with_no_args_spec.rb を実行すると:
Then 出力には以下のすべてが含まれているはずです:
| 6 つの例、3 つの失敗 | | 引数なしでブロックが yield されないことを期待しましたが、実際には yield されました | | 引数なしでブロックが yield されることを期待しましたが、実際には yield されませんでした | | 引数を伴ってブロックが yield されることを期待しましたが、実際には引数 ["a"] とともに yield されました |
yield_successive_args マッチャー
Given ファイル名が "yield_successive_args_spec.rb" のファイルがあるとき:
def array
[1, 2, 3]
end
def array_of_tuples
[[:a, :b], [:c, :d]]
end
RSpec.describe "yield_successive_args matcher" do
specify { expect { |b| array.each(&b) }.to yield_successive_args(1, 2, 3) }
specify { expect { |b| array_of_tuples.each(&b) }.to yield_successive_args([:a, :b], [:c, :d]) }
specify { expect { |b| array.each(&b) }.to yield_successive_args(Integer, Integer, Integer) }
specify { expect { |b| array.each(&b) }.not_to yield_successive_args(1, 2) }
# deliberate failures
specify { expect { |b| array.each(&b) }.not_to yield_successive_args(1, 2, 3) }
specify { expect { |b| array_of_tuples.each(&b) }.not_to yield_successive_args([:a, :b], [:c, :d]) }
specify { expect { |b| array.each(&b) }.not_to yield_successive_args(Integer, Integer, Integer) }
specify { expect { |b| array.each(&b) }.to yield_successive_args(1, 2) }
end
When rspec yield_successive_args_spec.rb を実行すると:
Then 出力には以下のすべてが含まれているはずです:
| 8 つの例、4 つの失敗 | | 引数を連続してブロックが yield されないことを期待しましたが、期待された引数で yield されました | | 引数を連続してブロックが yield されることを期待しましたが、予期しない引数で yield されました |