Matching arguments
Use with
to specify the expected arguments. A message expectation constrained by with
will only be satisfied when called with matching arguments. A canned response for an
allowed message 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>)` |
Basic example
Given a file named "basic_example_spec.rb" with:
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") |
Using keyword arguments
Given a file named "keyword_example_spec.rb" with:
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"}) |
Using keyword arguments on Rubies that differentiate hashes from keyword arguments
Given a file named "keyword_example_spec.rb" with:
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) |
Using a RSpec matcher
Given a file named "rspec_matcher_spec.rb" with:
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]) |
Using a custom matcher
Given a file named "custom_matcher_spec.rb" with:
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) |
Responding differently based on the arguments
Given a file named "responding_differently_spec.rb" with:
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.