Gio Polvara
Gio Polvara

Reputation: 26978

RSpec: expect.to receive fails if object is not referenced directly

In my Rails application I have a User model:

class User
  def self.foo
    User.all.each{ |user| user.bar }
  end

  def bar
  end
end

In my spec file I want to check that foo calls bar for every user, so far that's what I have:

describe '::foo' do
  let!(:users) { Fabricate.times(5, :user) }

  it 'calls bar for every user' do
    users.each do |user|
      expect(user).to receive(:bar)
    end

    User.foo
  end
end

Although the method gets called (I debugged it, so I'm sure of that) the spec is red.

Also I tried to write this code to understand where the problem was:

let!(:user) { Fabricate(:user) }

it 'fails' do
  expect(user).to receive(:bar)
  User.first.bar
end

it 'pass' do
  expect(user).to receive(:bar)
  user.bar
end

It seems that if I reference my instance directly it works, if I obtain it from the DB the expectation doesn't work.

I use mongoid, not sure if this is relevant.

Upvotes: 2

Views: 1361

Answers (1)

Andy Waite
Andy Waite

Reputation: 11076

I believe it cannot be done due to how RSpec works: When you set an expectation, RSpec essentially 'wraps' the object so that it can keep track of the messages it receives.

But when the implementation code fetches records from the database, they are not wrapped, so RSpec isn't able to record their messages.

RSpec does have a method allow_any_instance_of which can help in some cases, but its use is discouraged, and don't think it would be suitable here.

In this situation, I would suggest stubbing User.all to return some doubles (two should be sufficient). You can then verify that bar is called on each one.

Upvotes: 4

Related Questions