« back to all cards in this deck
Posted over 3 years ago. Visible to the public.

Test ActiveRecord callbacks with RSpec

Our example will be a class Project. Each project has an owner and many milestones. After saving, the project creates an initial milestone and notifies the owner of its creation.

class Project< ActiveRecord::Base belongs_to :owner has_many :milestones after_save :create_milestones after_save :notify_owner private def notify_owner owner.project_created! end def create_milestones milestones.create(:name => 'Milestone 1') end end

Here is a bad example of how to test this class:

describe Model, 'after_save' do it 'should create an initial milestone' do project = Project.new project.milestones.should_receive(:create) project.run_callbacks(:after_save) end it 'should notify its owner' do project = Project.new(:owner => mock_model(User)) project.owner.should_receive(:project_created!) project.run_callbacks(:after_save) end end

The test is bad because it tests too much at once. Each test has to deal with the side effects of every other after_save method. The first example will actually crash because it calls project_created on an owner that is nil.

You would much rather test the behaviour of each callback method in isolation. Then add another test that checks whether all expected methods are called.

describe Project do describe 'create_milestones' do it 'should create an initial milestone' do project = Project.new project.milestones.should_receive(:create) project.send(:create_milestones) end end describe 'notify_owner' do it 'should notify its owner' do project = Project.new(:owner => mock_model(User)) project.owner.should_receive(:project_created!) project.send(:notify_owner) end end describe 'after_save' do it 'should run the proper callbacks' do project = Project.new project.should_receive(:create_milestones) project.should_receive(:notify_owner) project.run_callbacks(:after_save) end end end

Note that you can also use the it_should_run_callbacks macro from our spec_candy helpers to check whether a given set of methods is registered for a callback.

Always try to only test one thing at a time. It will keep your examples focused and more resilient to change.

There is a dedicate note about testing state_machine callbacks.

Author of this card:

Avatar
Henning Koch
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
Posted by Henning Koch to makandropedia