Search code examples
rspecrspec-mocks

Stub a 'nested' class method that is called by a wrapper method (RSpec Mocks)


Situation: I want to stub a helper method, so that I can call a method that wraps it and get the stubbed response back.

Code is set up like this:

class Thing
  def self.method_one(foo)
    self.method_two(foo, 'some random string')
  end

  def self.method_two(foo, bar)
    self.method_three(foo, bar, 'no meaning')
  end

  def self.method_three(foo, bar, baz)
    "#{foo} is #{bar} with #{baz}"
  end
end

I'm trying to mock .method_three so that I can call .method_one and have it ultimately call .method_three's double instead of the real deal:

it "uses the mock for .method_three" do
  response_double = 'This is a different string'
  thing = class_double("Thing", :method_three => response_double).as_stubbed_const

  response = thing.method_one('Hi')
  expect(response).to eq(response_double)
end

The error I'm getting:

RSpec::Mocks::MockExpectationError: #<ClassDouble(Thing) (anonymous)> received unexpected message :method_one with ("Hi")

Is what I'm trying to do possible? Feels like I'm missing an obvious step, but despite my best efforts I haven't been able to find an example of this or a question that asks anything comparable.

(Note: if it matters, this is not a Rails project.)


Solution

  • You may want to use RSpec's allow(...) to stub the intermediate method. This is also useful for testing logic flow or for mocking third-party services in tests.

    For example: expected_response = 'This is a different string' allow(Thing).to receive(:method_three).and_return(expected_response)

    Then expect(Thing.method_one('Hi')).to eq(expected_response) should pass.

    See https://relishapp.com/rspec/rspec-mocks/v/2-14/docs/method-stubs for more information on stubbing methods.