Testing ActiveRecord Callbacks

I’m not a big fan of ActiveRecord callbacks – it usually seems like a quick-and-easy solution, but in the long term it makes testing difficult and can often create unexpected side-effects. However, sometimes there really is no other option which means we need to test these callbacks!

One way of testing the callbacks is to invoke the run_callbacks method.

describe 'after save callbacks' do
  it 'should create a price' do
    product = Product.new
    product.prices.should_receive(:create)
    product.run_callbacks(:after_save)
  end
end

However, this no longer seems to work in the latest version of RSpec – you will receive this error: NoMethodError: undefined method `_run_before_save_callbacks’

The API has changed slightly – you can now either run only the before_save callbacks, or run both the before_save and after_save callbacks.

To run only the before_save callbacks:

product = Product.new
product.run_callbacks(:save) { false }

To run both the before_save and after_save callbacks:

product = Product.new
product.run_callbacks(:save) { true }

However, I would argue that invoking the individual callbacks in this way is not the best way of testing the callbacks. I much rather prefer to define a private method for the callback and then invoking the private method.

class Product < ActiveRecord::Base
  after_save :create_price
  
  private
  
  def create_price
    # create the price here
  end
end

In this case, we can test the create_price method individually - still messy, but cleaner in my opinion.

describe '#create_price' do
  it 'should create a price' do
    product = Product.new
    product.prices.should_receive(:create)
    product.send(:create_price)
  end
end

Of course, the best solution is to avoid callbacks altogether. Happy coding.