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
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 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
end
Always try to only test one thing at a time. It will keep your examples focused and more resilient to change.