メインコンテンツまでスキップ

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 されました |