How to test callback methods in Rails
ActiveRecord models come with useful callbacks like after_save and before_validation. Here is how to properly test your callback methods.
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
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
endHere 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
endThe 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 check 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
endAlways try to only test one thing at a time. It will keep your examples focused and more resilient to change.
You can follow any response to this post through the Atom feed.


