Automatico
Automatico

Reputation: 12916

RSpec mocking all methods on object

I am working on some rspec tests now where one of my initialize parameters is a "foreign" class, meaning I have no clue where it comes from. It is injected from a framework I am using. In my code I need to utilize this object for various things, mostly/exclusively to call methods to pass data to this object (no data extraction).

Is it possible to completely mock an object so that I can verify that certain, undefined, methods are called.

#an example of what this object could look like
let(:my_obscure_object) { RSpec.complete_mock } #NOTE: This does not work
...
it "verify .whatever method is called" do
  expect(my_obscure_object).to receive(:whatever).with(some_data)
  my_class.work
end

it "verify .sub_whatever method is called" do
  expect(my_obscure_object.sub_thing).to receive(:sub_whatever).with(some_data)
  my_class.work2
end

The method I can think of currently to solve this is to make an object and stub every single possible method I might need, like this

#an example of a tedious method to solve my problem
let(:my_obscure_object) { Object.new }
before(:each) do
  my_obscure_object.stub(:whatever) { nil }
  my_obscure_object.stub(:whatever2) { nil }
  my_obscure_object.stub(:whatever3) { nil }
  my_obscure_object.stub(:whatever4) { nil }
  my_obscure_object.stub(:whatever5) { nil }
  ob = Object.new
  ob.stub(:sub_whatever) { nil }
  my_obscure_object.stub(:sub_thing) { ob }
end
...
it "verify .whatever method is called" do
  expect(my_obscure_object).to receive(:whatever).with(some_data)
  my_class.work
end
it "verify .sub_whatever method is called" do
  expect(my_obscure_object.sub_thing).to receive(:sub_whatever).with(some_data)
  my_class.work2
end

Is there any solutions to my problem?

Upvotes: 1

Views: 652

Answers (1)

Peter Alfvin
Peter Alfvin

Reputation: 29399

You can create a test double which will accept all method calls using as_null_object, as follows:

describe "SO example" do
  #an example of what this object could look like
  let(:my_obscure_object) { double("obscure").as_null_object }
  let(:some_data) { 'foobar'}

  it "verify .whatever method is called" do
    expect(my_obscure_object).to receive(:whatever).with(some_data)
    my_obscure_object.whatever(some_data)
  end

  it "verify .sub_whatever method is called" do
    expect(my_obscure_object).to receive_message_chain(:whatever, :sub_whatever)
    my_obscure_object.whatever.sub_whatever
  end

end

Note that the receive_message_chain method is only available as of RSpec 3.0.0.beta2 available from github and does not allow you to specify any expected argument values. You can rewrite that expectation using individual test doubles if you want to use an older version of RSpec or specify the arguments passed.

Upvotes: 3

Related Questions