Search code examples
ruby-on-railsunit-testingruby-on-rails-4rspecrspec-rails

Rails model test - should_receive method not working


I'm writing a pretty straightforward method. Whenever a referer has referred 5 people to become new users, I want them to get a refund. This means that when a new user is created, there's a method check_referer that checks to see if the person who referred them (if this person exists) should get a refund because they've now referred 5 people in total.

In the test logs, based on the puts statement, I can tell that the code is working and the refund_membership_fees_paid method is indeed being called once. But the test keeps failing with:

Failure/Error: @referer.should_receive(:refund_membership_fees_paid).exactly(1).times
   (#<User:0x007fbf46bf1c58>).refund_membership_fees_paid(any args)
       expected: 1 time with any arguments
       received: 0 times with any arguments

Test code:

describe User, "Test refund_membership_fees_paid method is called" do
  before do
    @referer = User.new()
    @referer.save(validate:false)
    RefererRefundAlert.stub_chain(:new, :async, :perform)
  end
  it "at 5 users" do
    5.times do |index|
        u = User.new(referred_by: @referer.id)
        u.save(validate:false)
    end
    @referer.should_receive(:refund_membership_fees_paid).exactly(1).times
  end
end

Model code:

def check_referer
  if self.referred_by.present? && User.where(referred_by: self.referred_by).count == 5
    User.find(self.referred_by).refund_membership_fees_paid
  end
end

def refund_membership_fees_paid
  puts "refund_membership_fees_paid method"
  RefererRefundAlert.new.async.perform(self.id)
end

Solution

  • User.find does not return the same object as @referer; it will return a different instance of User that represents the same user in the database.

    Instead of checking whether refund_membership_fees_paid is called, you can verify that the correct user ID is getting passed intoRefererRefundAlert.new.async.perform.

    Also, as others mentioned, you should set your expectation before running the tested methods.

    RefererRefundAlert.new.async.should_receive(:perform)
          .with(@referer.id).exactly(1).times
    5.times do |index|
      u = User.new(referred_by: @referer.id)
      u.save(validate:false)
    end