Any Instance

rspec-mocks provides two methods, allow_any_instance_of and expect_any_instance_of, that will allow you to stub or mock any instance of a class. They are used in place of allow or expect:

  allow_any_instance_of(Widget).to receive(:name).and_return("Wibble")
  expect_any_instance_of(Widget).to receive(:name).and_return("Wobble")

These methods add the appropriate stub or expectation to all instances of Widget.

You can also configure the responses in the same manner.

This feature is sometimes useful when working with legacy code, though in general we discourage its use for a number of reasons:

Use allow_any_instance_of to stub a method

Given a file named “example_spec.rb” with:

RSpec.describe "allow_any_instance_of" do
  it "returns the specified value on any instance of the class" do
    allow_any_instance_of(Object).to receive(:foo).and_return(:return_value)

    o = Object.new
    expect(o.foo).to eq(:return_value)
  end
end

When I run rspec example_spec.rb

Then the examples should all pass.

Use allow_any_instance_of to stub multiple methods

Given a file named “example_spec.rb” with:

RSpec.describe "allow_any_instance_of" do
  context "with receive_messages" do
    it "stubs multiple methods" do
      allow_any_instance_of(Object).to receive_messages(:foo => 'foo', :bar => 'bar')

      o = Object.new
      expect(o.foo).to eq('foo')
      expect(o.bar).to eq('bar')
    end
  end
end

When I run rspec example_spec.rb

Then the examples should all pass.

Stubbing any instance of a class with specific arguments

Given a file named “example_spec.rb” with:

RSpec.describe "allow_any_instance_of" do
  context "with arguments" do
    it "returns the stubbed value when arguments match" do
      allow_any_instance_of(Object).to receive(:foo).with(:param_one, :param_two).and_return(:result_one)
      allow_any_instance_of(Object).to receive(:foo).with(:param_three, :param_four).and_return(:result_two)

      o = Object.new
      expect(o.foo(:param_one, :param_two)).to eq(:result_one)
      expect(o.foo(:param_three, :param_four)).to eq(:result_two)
    end
  end
end

When I run rspec example_spec.rb

Then the examples should all pass.

Block implementation is passed the receiver as first arg

Given a file named “example_spec.rb” with:

RSpec.describe "allow_any_instance_of" do
  it 'yields the receiver to the block implementation' do
    allow_any_instance_of(String).to receive(:slice) do |instance, start, length|
      instance[start, length]
    end

    expect('string'.slice(2, 3)).to eq('rin')
  end
end

When I run rspec example_spec.rb

Then the examples should all pass.

Use expect_any_instance_of to set a message expectation on any instance

Given a file named “example_spec.rb” with:

RSpec.describe "expect_any_instance_of" do
  before do
    expect_any_instance_of(Object).to receive(:foo)
  end

  it "passes when an instance receives the message" do
    Object.new.foo
  end

  it "fails when no instance receives the message" do
    Object.new.to_s
  end
end

When I run rspec example_spec.rb

Then it should fail with the following output:

2 examples, 1 failure
Exactly one instance should have received the following message(s) but didn’t: foo

Specify different return values for multiple calls in combination with allowanyinstance_of

Using the multiple calls feature with allow_any_instance_of results in the behaviour where multiple calls are configured on every instance. Therefore, each individual instance will return the configured return values in the order specified, and then begin to repeat the last value.

Given a file named “multiplecallsspecwithallowanyinstance_of.rb” with:

class SomeClass
end

RSpec.describe "When the method is called multiple times on different instances with allow_any_instance_of" do
  it "demonstrates the mocked behavior on each instance individually" do
    allow_any_instance_of(SomeClass).to receive(:foo).and_return(1, 2, 3)

    first = SomeClass.new
    second = SomeClass.new
    third = SomeClass.new

    expect(first.foo).to eq(1)
    expect(second.foo).to eq(1)

    expect(first.foo).to eq(2)
    expect(second.foo).to eq(2)

    expect(first.foo).to eq(3)
    expect(first.foo).to eq(3) # repeats last value from here
    expect(second.foo).to eq(3)
    expect(second.foo).to eq(3) # repeats last value from here

    expect(third.foo).to eq(1)
    expect(third.foo).to eq(2)
    expect(third.foo).to eq(3)
    expect(third.foo).to eq(3) # repeats last value from here
  end
end

When I run rspec multiple_calls_spec_with_allow_any_instance_of.rb

Then the examples should all pass.