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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
Feature: Matching arguments
Use `with` to specify the expected arguments. A [message expectation](../basics/expecting-messages) constrained by `with`
will only be satisfied when called with matching arguments. A canned response for an
[allowed message](../basics/allowing-messages) will only be used when the arguments match.
| To match... | ...use an expression like: | ...which matches calls like: |
|-----------------------------------------------------|------------------------------------|-----------------------------------------------------|
| Literal arguments | `with(1, true)` | `foo(1, true)` |
| Literal arguments where one is a hash | `with(1, {x: 1, y: 2})` | `foo(1, x: 1, y: 2) (where last argument is a hash) |
| Keyword arguments | `with(x: 1, y: 2)` | `foo(x: 1, y: 2)` (where x and y are keywords) |
| Anything that supports case equality (`===`) | `with(/bar/)` | `foo("barn")` |
| Any list of args | `with(any_args)` | `foo()`<br>`foo(1)`<br>`foo(:bar, 2)` |
| Any sublist of args (like an arg splat) | `with(1, any_args)` | `foo(1)`<br>`foo(1, :bar, :bazz)` |
| An empty list of args | `with(no_args)` | `foo()` |
| Anything for a given positional arg | `with(3, anything)` | `foo(3, nil)`<br>`foo(3, :bar)` |
| Against an interface | `with(duck_type(:each))` | `foo([])` |
| A boolean | `with(3, boolean)` | `foo(3, true)`<br>`foo(3, false)` |
| A subset of a hash | `with(hash_including(:a => 1))` | `foo(:a => 1, :b => 2)` |
| An excluded subset of a hash | `with(hash_excluding(:a => 1))` | `foo(:b => 2)` |
| A subset of an array | `with(array_including(:a, :b))` | `foo([:a, :b, :c])` |
| An excluded subset of an array | `with(array_excluding(:a, :b))` | `foo([:c, :d])` |
| An instance of a specific class | `with(instance_of(Integer))` | `foo(3)` |
| An object with a given module in its ancestors list | `with(kind_of(Numeric))` | `foo(3)` |
| An object with matching attributes | `with(having_attributes(:a => 1))` | `foo(:a => 1, :b => 2)` |
| Any RSpec matcher | `with(<matcher>)` | `foo(<object that matches>)` |
Scenario: Basic example
Given a file named "basic_example_spec.rb" with:
"""ruby
RSpec.describe "Constraining a message expectation using with" do
let(:dbl) { double }
before { expect(dbl).to receive(:foo).with(1, anything, /bar/) }
it "passes when the args match" do
dbl.foo(1, nil, "barn")
end
it "fails when the args do not match" do
dbl.foo(1, nil, "other")
end
end
"""
When I run `rspec basic_example_spec.rb`
Then it should fail with the following output:
| 2 examples, 1 failure |
| |
| Failure/Error: dbl.foo(1, nil, "other") |
| #<Double (anonymous)> received :foo with unexpected arguments |
| expected: (1, anything, /bar/) |
| got: (1, nil, "other") |
@kw-arguments
Scenario: Using keyword arguments
Given a file named "keyword_example_spec.rb" with:
"""ruby
class WithKeywords
def foo(bar: "")
end
end
RSpec.describe "Constraining a message expectation using with" do
let(:dbl) { instance_double(WithKeywords) }
before { expect(dbl).to receive(:foo).with(bar: "baz") }
it "passes when the args match" do
dbl.foo(bar: "baz")
end
it "fails when the args do not match" do
dbl.foo(bar: "incorrect")
end
end
"""
When I run `rspec keyword_example_spec.rb`
Then it should fail with the following output:
| 2 examples, 1 failure |
| |
| Failure/Error: dbl.foo(bar: "incorrect") |
| #<InstanceDouble(WithKeywords) (anonymous)> received :foo with unexpected arguments |
| expected: ({:bar=>"baz"}) |
| got: ({:bar=>"incorrect"}) |
@distincts_kw_args_from_positional_hash
Scenario: Using keyword arguments on Rubies that differentiate hashes from keyword arguments
Given a file named "keyword_example_spec.rb" with:
"""ruby
class WithKeywords
def foo(bar: "")
end
end
RSpec.describe "Constraining a message expectation using with" do
let(:dbl) { instance_double(WithKeywords) }
before { expect(dbl).to receive(:foo).with(bar: "baz") }
it "fails when the args do not match due to a hash" do
dbl.foo({bar: "also incorrect"})
end
end
"""
When I run `rspec keyword_example_spec.rb`
Then it should fail with the following output:
| 1 example, 1 failure |
| |
| Failure/Error: dbl.foo({bar: "also incorrect"}) |
| #<InstanceDouble(WithKeywords) (anonymous)> received :foo with unexpected arguments |
| expected: ({:bar=>"baz"}) (keyword arguments) |
| got: ({:bar=>"also incorrect"}) (options hash) |
Scenario: Using a RSpec matcher
Given a file named "rspec_matcher_spec.rb" with:
"""ruby
RSpec.describe "Using a RSpec matcher" do
let(:dbl) { double }
before { expect(dbl).to receive(:foo).with(a_collection_containing_exactly(1, 2)) }
it "passes when the args match" do
dbl.foo([2, 1])
end
it "fails when the args do not match" do
dbl.foo([1, 3])
end
end
"""
When I run `rspec rspec_matcher_spec.rb`
Then it should fail with the following output:
| 2 examples, 1 failure |
| |
| Failure/Error: dbl.foo([1, 3]) |
| #<Double (anonymous)> received :foo with unexpected arguments |
| expected: (a collection containing exactly 1 and 2) |
| got: ([1, 3]) |
@ripper
Scenario: Using satisfy for complex custom expecations
Given a file named "rspec_satisfy_spec.rb" with:
"""ruby
RSpec.describe "Using satisfy for complex custom expecations" do
let(:dbl) { double }
def a_b_c_equals_5
satisfy { |data| data[:a][:b][:c] == 5 }
end
it "passes when the expectation is true" do
expect(dbl).to receive(:foo).with(a_b_c_equals_5)
dbl.foo({ :a => { :b => { :c => 5 } } })
end
it "fails when the expectation is false" do
expect(dbl).to receive(:foo).with(a_b_c_equals_5)
dbl.foo({ :a => { :b => { :c => 3 } } })
end
end
"""
When I run `rspec rspec_satisfy_spec.rb`
Then it should fail with the following output:
| 2 examples, 1 failure |
| |
| Failure/Error: dbl.foo({ :a => { :b => { :c => 3 } } }) |
| #<Double (anonymous)> received :foo with unexpected arguments |
| expected: (satisfy expression `data[:a][:b][:c] == 5`) |
| got: ({:a=>{:b=>{:c=>3}}}) |
Scenario: Using a custom matcher
Given a file named "custom_matcher_spec.rb" with:
"""ruby
RSpec::Matchers.define :a_multiple_of do |x|
match { |actual| (actual % x).zero? }
end
RSpec.describe "Using a custom matcher" do
let(:dbl) { double }
before { expect(dbl).to receive(:foo).with(a_multiple_of(3)) }
it "passes when the args match" do
dbl.foo(12)
end
it "fails when the args do not match" do
dbl.foo(13)
end
end
"""
When I run `rspec custom_matcher_spec.rb`
Then it should fail with the following output:
| 2 examples, 1 failure |
| |
| Failure/Error: dbl.foo(13) |
| #<Double (anonymous)> received :foo with unexpected arguments |
| expected: (a multiple of 3) |
| got: (13) |
Scenario: Responding differently based on the arguments
Given a file named "responding_differently_spec.rb" with:
"""ruby
RSpec.describe "Using #with to constrain responses" do
specify "its response depends on the arguments" do
dbl = double
# Set a default for any unmatched args
allow(dbl).to receive(:foo).and_return(:default)
allow(dbl).to receive(:foo).with(1).and_return(1)
allow(dbl).to receive(:foo).with(2).and_return(2)
expect(dbl.foo(0)).to eq(:default)
expect(dbl.foo(1)).to eq(1)
expect(dbl.foo(2)).to eq(2)
end
end
"""
When I run `rspec responding_differently_spec.rb`
Then the examples should all pass
|