4

I need to stub all instances of a model that have a particular attribute or set of attributes. For example, using ActiveRecord:

let(:model1) { Model.create!(uid: 1) }
let(:model2) { Model.create!(uid: 2) }

before do
  allow(model1).to receive(:foo).and_return :bar 
  allow(model2).to receive(:foo).and_return :baz
end

it do
  expect(model1.foo).to eq :bar # => passes
  expect(model2.foo).to eq :baz # => passes

  ################################################
  #### Here is the issue I'm trying to solve: ####
  ################################################
  new_instance_of_model1 = Model.find_by(uid: 1)
  new_instance_of_model2 = Model.find_by(uid: 2)

  expect(new_instance_of_model1.foo).to eq :bar # => fails
  expect(new_instance_of_model2.foo).to eq :baz # => fails
end

Is there some way to stub all instances of Model that have uid: 1?

I'm looking for something like:

allow_any_instance_of(Model).with_attributes(uid: 1).to receive(:foo).and_return(:bar)
allow_any_instance_of(Model).with_attributes(uid: 2).to receive(:foo).and_return(:baz)

Note:

I can't use something like:

allow(Model).to receive(:find).with(1)and_return(model1)
allow(Model).to receive(:find).with(2)and_return(model2)

because there are many other ways to get to the model (associations, scopes, Arel, etc.)

stevenspiel
  • 5,775
  • 13
  • 60
  • 89
  • I can try and give an advice (stupid one though, but it may solve your problem). You can try creating a method inside your spec: `def stub(uid); allow(Model.any_instance).to receive(:foo).and_return :baz if uid == 1; end`. Then you stub specific models by passing the `uid` in the function – uno_ordinary Mar 31 '17 at 15:28

1 Answers1

3

This isn't a very good idea, but you can accomplish this by passing a block argument to receive instead of a method chain, like this:

allow_any_instance_of(Model).to receive(:foo) do |model|
  case model.uid
  when 1
    :bar
  when 2
    :baz
  end
end
brainbag
  • 1,007
  • 9
  • 23