Samuel Wong

Ramblings of a rocket scientist turned web developer

How to Test ActiveModel Callbacks With RSpec

In unstash, every time a person (say, the owner of the item) makes a change to a loan request (i.e. he approves it), the other person involved in the loan (in this case, the borrower) is notified.

This is easy in Rails. In my Loans model, I have an after_save callback that runs LoanNotification.create_from_loan(self). I’ve purposely delegated the task of creating the actual notification and determining the recipient away from Loan. This is because Loans shouldn’t be responsible for how LoanNotifications are created (SRP).

Here’s how I implemented this in RSpec.

My first attempt:

describe Loan do
  describe 'after_save', :focus do
    it "creates a loan notification" do
      @loan = FactoryGirl.build(:loan)
      LoanNotification.should_receive(:create_from_loan)
      @loan.save!
    end
  end
end

But this is brittle because I might one day decide to rename create_from_loan and cause the test to break. What I really want to test is the effect of after_save, which is saving a LoanNotification object to the database.

The better way:

describe Loan do
  describe 'after_save', :focus do
    it "creates a loan notification" do
      expect {
        @loan = FactoryGirl.build(:loan)
        @loan.save!
      }.to change{ LoanNotification.all.count }.by(1)
    end
  end
end

This gets at the core of what I want to test and clearly tells us that Loan is not responsible for how the LoanNotification is created, but only that it is created.

In my LoanNotification spec, I can test that the recipient is correctly identified and any data is saved correctly.

Comments