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()
foo(1)
foo(:bar, 2)
Any sublist of args (like an arg splat) with(1, any_args) foo(1)
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)
foo(3, :bar)
Against an interface with(duck_type(:each)) foo([])
A boolean with(3, boolean) foo(3, true)
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 “basicexamplespec.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”)
# received :foo with unexpected arguments
expected: (1, anything, /bar/)
got: (1, nil, “other”)

Using keyword arguments

Given a file named “keywordexamplespec.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”)
# 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 “keywordexamplespec.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”})
# received :foo with unexpected arguments
expected: ({:bar=>“baz”}) (keyword arguments)
got: ({:bar=>“also incorrect”}) (options hash)

Using a RSpec matcher

Given a file named “rspecmatcherspec.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])
# received :foo with unexpected arguments
expected: (a collection containing exactly 1 and 2)
got: ([1, 3])

Using satisfy for complex custom expecations

Given a file named “rspecsatisfyspec.rb” with:

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 } } })
# received :foo with unexpected arguments
expected: (satisfy expression data[:a][:b][:c] == 5)
got: ({:a=>{:b=>{:c=>3}}})

Using a custom matcher

Given a file named “custommatcherspec.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)
# received :foo with unexpected arguments
expected: (a multiple of 3)
got: (13)

Responding differently based on the arguments

Given a file named “respondingdifferentlyspec.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.