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

スパイ

メッセージの期待値は、コードの実行前に例の期待値を設定します。多くの開発者は、テストの構造化にはarrange-act-assert(またはgiven-when-then)パターンを使用することを好みます。スパイは、このパターンをサポートする別のタイプのテストダブルであり、後からメッセージが受信されたことを期待することができます。これは、have_receivedを使用して行います。

スパイとして任意のテストダブル(または部分的なダブル)を使用することができますが、ダブルは気になるメッセージをスパイするようにセットアップする必要があります。スパイは自動的にすべてのメッセージをスパイしますが、メッセージを許可することもできます。

have_receivedは、通常のメッセージの期待値と同じ流暢なインターフェースをサポートしています。

注意:ここで示されているhave_receivedのAPIは、rspec-expectationsを使用している場合にのみ機能します。 注意:スパイが受信したメッセージの記録後に引数が変更されると、have_received(...).with(...)は正しく機能しません。

スパイの使用方法

次の内容で「spy_spec.rb」という名前のファイルを作成します。

RSpec.describe "have_received" do
it "passes when the message has been received" do
invitation = spy('invitation')
invitation.deliver
expect(invitation).to have_received(:deliver)
end
end

When rspec spy_spec.rbを実行する

Then すべての例がパスするはずです。

部分的なダブルのメソッドをスパイする

次の内容で「partial_double_spy_spec.rb」という名前のファイルを作成します。

class Invitation
def self.deliver; end
end

RSpec.describe "have_received" do
it "passes when the expectation is met" do
allow(Invitation).to receive(:deliver)
Invitation.deliver
expect(Invitation).to have_received(:deliver)
end
end

When rspec partial_double_spy_spec.rbを実行する

Then すべての例がパスするはずです。

メッセージが受信されていない場合の失敗

次の内容で「failure_spec.rb」という名前のファイルを作成します。

class Invitation
def self.deliver; end
end

RSpec.describe "failure when the message has not been received" do
example "for a spy" do
invitation = spy('invitation')
expect(invitation).to have_received(:deliver)
end

example "for a partial double" do
allow(Invitation).to receive(:deliver)
expect(Invitation).to have_received(:deliver)
end
end

When rspec failure_spec.rb --order definedを実行する

Then 次のエラーが発生するはずです。

  1) failure when the message has not been received for a spy
Failure/Error: expect(invitation).to have_received(:deliver)

(Double "invitation").deliver(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments

And 次のエラーが発生するはずです。

  2) failure when the message has not been received for a partial double
Failure/Error: expect(Invitation).to have_received(:deliver)

(Invitation (class)).deliver(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments

流暢なインターフェースを使用した制約の設定

次の内容で「setting_constraints_spec.rb」という名前のファイルを作成します。

RSpec.describe "An invitation" do
let(:invitation) { spy("invitation") }

before do
invitation.deliver("foo@example.com")
invitation.deliver("bar@example.com")
end

it "passes when a count constraint is satisfied" do
expect(invitation).to have_received(:deliver).twice
end

it "passes when an order constraint is satisfied" do
expect(invitation).to have_received(:deliver).with("foo@example.com").ordered
expect(invitation).to have_received(:deliver).with("bar@example.com").ordered
end

it "fails when a count constraint is not satisfied" do
expect(invitation).to have_received(:deliver).at_least(3).times
end

it "fails when an order constraint is not satisfied" do
expect(invitation).to have_received(:deliver).with("bar@example.com").ordered
expect(invitation).to have_received(:deliver).with("foo@example.com").ordered
end
end

When rspec setting_constraints_spec.rb --order definedを実行する

Then 次の出力で失敗するはずです。

| 4 examples, 2 failures                                                                       |
| |
| 1) An invitation fails when a count constraint is not satisfied |
| Failure/Error: expect(invitation).to have_received(:deliver).at_least(3).times |
| (Double "invitation").deliver(*(any args)) |
| expected: at least 3 times with any arguments |
| received: 2 times with any arguments |
| |
| 2) An invitation fails when an order constraint is not satisfied |
| Failure/Error: expect(invitation).to have_received(:deliver).with("foo@example.com").ordered |
| #<Double "invitation"> received :deliver out of order |