1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
Feature: Spies
[Message expectations](./expecting-messages) put an example's expectation at the start, before you've invoked the
code-under-test. Many developers prefer using an arrange-act-assert (or given-when-then)
pattern for structuring tests. Spies are an alternate type of test double that support this
pattern by allowing you to expect that a message has been received after the fact, using
`have_received`.
You can use any test double (or partial double) as a spy, but the double must be setup to
spy on the messages you care about. Spies automatically spy on all messages,
or you can [allow a message](./allowing-messages) to spy on it.
`have_received` supports the same fluent interface for [setting constraints](../setting-constraints) that normal message expectations do.
Note: The `have_received` API shown here will only work if you are using rspec-expectations.
Note: `have_received(...).with(...)` is unable to work properly when passed arguments are mutated after the spy records the received message.
Scenario: Using a spy
Given a file named "spy_spec.rb" with:
"""ruby
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 I run `rspec spy_spec.rb`
Then the examples should all pass
Scenario: Spy on a method on a partial double
Given a file named "partial_double_spy_spec.rb" with:
"""ruby
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 I run `rspec partial_double_spy_spec.rb`
Then the examples should all pass
Scenario: Failure when the message has not been received
Given a file named "failure_spec.rb" with:
"""ruby
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 I run `rspec failure_spec.rb --order defined`
Then it should fail with:
"""
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 it should fail with:
"""
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
"""
Scenario: Set constraints using the fluent interface
Given a file named "setting_constraints_spec.rb" with:
"""ruby
RSpec.describe "An invitiation" 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 satisifed" 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 satisifed" 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 I run `rspec setting_constraints_spec.rb --order defined`
Then it should fail with the following output:
| 4 examples, 2 failures |
| |
| 1) An invitiation 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 invitiation fails when an order constraint is not satisifed |
| Failure/Error: expect(invitation).to have_received(:deliver).with("foo@example.com").ordered |
| #<Double "invitation"> received :deliver out of order |
|