26

I've searched a lot and just cannot figure this out although it seems basic. Here's a way simplified example of what I want to do.

Create a simple method that does something but doesn't return anything, such as:

class Test
  def test_method(param)
    puts param
  end
  test_method("hello")
end

But in my rspec test I need to pass a different parameter, such as "goodbye" instead of "hello." I know this has to do with stubs and mocks, and I've looking over the documentation but can't figure it out: https://relishapp.com/rspec/rspec-mocks/v/3-0/docs/method-stubs

If I do:

@test = Test.new
allow(@test).to_receive(:test_method).with("goodbye")

it tells me to stub out a default value but I can't figure out how to do it correctly.

Error message:

received :test_method with unexpected arguments
  expected: ("hello")
  got: ("goodbye")
Please stub a default value first if message might be received with other args as well.     

I am using rspec 3.0, and calling something like

@test.stub(:test_method)

is not allowed.

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
user3060126
  • 461
  • 1
  • 4
  • 13

2 Answers2

44

How to set a default value that is explained at

and_call_original can configure a default response that can be overriden for specific args

require 'calculator'

RSpec.describe "and_call_original" do
  it "can be overriden for specific arguments using #with" do
    allow(Calculator).to receive(:add).and_call_original
    allow(Calculator).to receive(:add).with(2, 3).and_return(-5)

    expect(Calculator.add(2, 2)).to eq(4)
    expect(Calculator.add(2, 3)).to eq(-5)
  end
end

Source where I came to know about that can be found at https://makandracards.com/makandra/30543-rspec-only-stub-a-method-when-a-particular-argument-is-passed

Jignesh Gohel
  • 6,236
  • 6
  • 53
  • 89
2

For your example, since you don't need to test the actual result of test_method, only that puts gets called in it passing in param, I would just test by setting up the expectation and running the method:

class Test
  def test_method(param)
    puts param
  end
end

describe Test do
  let(:test) { Test.new }

  it 'says hello via expectation' do
    expect(test).to receive(:puts).with('hello')
    test.test_method('hello')
  end

  it 'says goodbye via expectation' do
    expect(test).to receive(:puts).with('goodbye')
    test.test_method('goodbye')
  end
end

What it seems you're attempting to do is set up a test spy on the method, but then I think you're setting up the method stub one level too high (on test_method itself instead of the call to puts inside test_method). If you put the stub on the call to puts, your tests should pass:

describe Test do
  let(:test) { Test.new }

  it 'says hello using a test spy' do
    allow(test).to receive(:puts).with('hello')
    test.test_method('hello')
    expect(test).to have_received(:puts).with('hello')
  end

  it 'says goodbye using a test spy' do
    allow(test).to receive(:puts).with('goodbye')
    test.test_method('goodbye')
    expect(test).to have_received(:puts).with('goodbye')
  end
end
Paul Fioravanti
  • 16,423
  • 7
  • 71
  • 122
  • 1
    Thanks - this is helpful! For one, I didn't know that you could say `expect(test).to receive(:puts)` - I thought you could only call the straight method (`test_method` here) so that's good to know. In the end my actual problem was more complex and I decided that I just needed to change my code to define method that *did* return a value, which is probably better practice anyway. And then it's easy to use `allow(test).to receive(:test_method).and_return('goodbye')`. I'm sure there's another way using some of this, and it's also good to know about test spies - I hadn't come across that before. – user3060126 Apr 18 '14 at 17:21