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

ブロックの実装

ブロックを渡すと、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を実行すると

その結果、すべての例がパスするはずです。