ブロックの実装
ブロックを渡すと、RSpecはそのブロックをメソッドの実装として使用します。呼び出し元が提供した引数(またはブロック)は、ブロックの実装によってイールドされます。この機能は非常に柔軟であり、より宣言的なフルエントインターフェースでは直接サポートされていない多くのユースケースをサポートしています。
フルエントインターフェースのメソッドには、いずれのメソッドにもブロックを渡すことができます。
allow(dbl).to receive(:foo) { do_something }
allow(dbl).to receive(:foo).with("args") { do_something }
allow(dbl).to receive(:foo).once { do_something }
allow(dbl).to receive(:foo).ordered { do_something }
ブロックの実装の一部の一般的なユースケースを以下に示しますが、これは網羅的なリストではありません。
簡潔な構文で戻り値を指定するためにブロックを使用する
「return_value_spec.rb」という名前のファイルがある場合、以下のようになります。
RSpec.describe "Specifying a return value using a block" do
it "returns the block's return value" do
dbl = double
allow(dbl).to receive(:foo) { 14 }
expect(dbl.foo).to eq(14)
end
end
「rspec return_value_spec.rb」と実行すると、すべての例がパスするはずです。
引数を検証するためにブロックを使用する
「verify_arguments_spec.rb」という名前のファイルがある場合、以下のようになります。
RSpec.describe "Verifying arguments using a block" do
it "fails when the arguments do not meet the expectations set in the block" do
dbl = double
allow(dbl).to receive(:foo) do |arg|
expect(arg).to eq("bar")
end
dbl.foo(nil)
end
end
「rspec verify_arguments_spec.rb」と実行すると、以下のエラーが発生するはずです。
Failure/Error: expect(arg).to eq("bar")
計算を実行するためにブロックを使用する
「perform_calculation_spec.rb」という名前のファイルがある場合、以下のようになります。
RSpec.describe "Performing a calculation using a block" do
it "returns the block's return value" do
loan = double("Loan", :amount => 100)
allow(loan).to receive(:required_payment_for_rate) do |rate|
loan.amount * rate
end
expect(loan.required_payment_for_rate(0.05)).to eq(5)
expect(loan.required_payment_for_rate(0.1)).to eq(10)
end
end
「rspec perform_calculation_spec.rb」と実行すると、すべての例がパスするはずです。
呼び出し元のブロックにイールドする
「yield_to_caller_spec.rb」という名前のファイルがある場合、以下のようになります。
RSpec.describe "When the caller passes a block" do
it "can be yielded to from your implementation block" do
dbl = double
allow(dbl).to receive(:foo) { |&block| block.call(14) }
expect { |probe| dbl.foo(&probe) }.to yield_with_args(14)
end
end
「rspec yield_to_caller_spec.rb」と実行すると、すべての例がパスするはずです。
パーシャルダブルの元の実装をブロック内でデリゲートする
「delegate_to_original_spec.rb」という名前のファイルがある場合、以下のようになります。
class Calculator
def self.add(x, y)
x + y
end
end
RSpec.describe "When using a block implementation on a partial double" do
it "supports delegating to the original implementation" do
original_add = Calculator.method(:add)
allow(Calculator).to receive(:add) do |x, y|
original_add.call(x, y) * 2
end
expect(Calculator.add(2, 5)).to eq(14)
end
end
「rspec delegate_to_original_spec.rb」と実行すると、すべての例がパスするはずです。
一時的なネットワークの障害をシミュレートする
「simulate_transient_network_failure_spec.rb」という名前のファイルがある場合、以下のようになります。
RSpec.describe "An HTTP API client" do
it "can simulate transient network failures" do
client = double("MyHTTPClient")
call_count = 0
allow(client).to receive(:fetch_data) do
call_count += 1
call_count.odd? ? raise("timeout") : { :count => 15 }
end
expect { client.fetch_data }.to raise_error("timeout")
expect(client.fetch_data).to eq(:count => 15)
expect { client.fetch_data }.to raise_error("timeout")
expect(client.fetch_data).to eq(:count => 15)
end
end
rspec simulate_transient_network_failure_spec.rb
を実行すると
その結果、すべての例がパスするはずです。