tdgs
tdgs

Reputation: 1096

RSpec `should_receive` behaviour with multiple method invocation

Consider the following test:

class A
  def print(args)
    puts args
  end
end

describe A do
  let(:a) {A.new}

  it "receives print" do
   a.should_receive(:print).with("World").and_call_original

   a.print("Hello")
   a.print("World")
 end
end

The RSpec Documentation says:

Use should_receive() to set an expectation that a receiver should receive a message before the example is completed.

So I was expecting this test to pass, but it is not. It is failing with the following message:

Failures:

1) A receives print
 Failure/Error: a.print("Hello")
   #<A:0x007feb46283190> received :print with unexpected arguments
     expected: ("World")
          got: ("Hello")

Is this expected behaviour? Is there a way to make this test pass?

I am using ruby 1.9.3p374 and rspec 2.13.1

Upvotes: 4

Views: 2928

Answers (4)

Daniel Le
Daniel Le

Reputation: 1830

How about adding allow(a).to receive(:print)?

require 'rspec'

class A
  def print(args)
    puts args
  end
end

describe A do
  let(:a) { described_class.new }

  it 'receives print' do
    allow(a).to receive(:print)
    expect(a).to receive(:print).with('World')

    a.print('Hello')
    a.print('World')
  end
end

Basically, allow(a).to_receive(:print) allows "a" to receive a "print" message with any arguments. Thus a.print('Hello') doesn't fail the test.

Upvotes: 4

Chris Heald
Chris Heald

Reputation: 62648

should_receive checks not only that the expected method was called, but that unexpected methods are not called. Simply add a specification for each invocation you expect:

class A
  def print(args)
    puts args
  end
end

describe A do
  let(:a) {A.new}

  it "receives print" do
   a.should_receive(:print).with("World").and_call_original
   a.should_receive(:print).with("Hello").and_call_original

   a.print("Hello")
   a.print("World")
 end
end

Upvotes: 0

Angrysheep
Angrysheep

Reputation: 422

How about this?

class A
  def print(args)
    puts args
  end
end

describe A do
  let(:a) {A.new}

  it "receives print" do
   a.should_receive(:print).with("World").and_call_original
   # it's important that this is after the real expectation
   a.should_receive(:print).any_number_of_times

   a.print("Hello")
   a.print("World")
 end
end

It adds a second expectation, which you may want to avoid. However, in consideration of @vrinek's question, this solution has the advantage of providing the correct failure message (expected: 1 time, received: 0 times). Cheers!

Upvotes: 2

Ju Liu
Ju Liu

Reputation: 3999

This should work:

class A
  def print(args)
    puts args
  end
end

describe A do
  let(:a) {A.new}

  it "receives print" do
   a.stub(:print).with(anything())
   a.should_receive(:print).with("World").and_call_original

   a.print("Hello")
   a.print("World")
 end
end

The test was failing because you had set a precise expectation "a should receive :print with 'World'", but rspec noticed that the a object was receiving the print method with 'Hello' therefore it failed the test. In my solution, I allow the print method to be invoked with any argument, but it still keeps track of the call with "World" as argument.

Upvotes: 4

Related Questions