The information in this card is out of date. Today we would rather make a few database queries than have a fragile test full of stubs.
You should test the callback methods and its correct invocation in two separate tests. Understand the ActiveRecord note before you move on with this note.
Say this is your Spaceship
class with a transition launch
and a release_docking_clamps
callback:
class Spaceship
state_machine :state, :initial => :docked do
event :launch do
transition :docked => :en_route
end
before_transition :on => :launch, :do => :release_docking_clamps
end
end
Testing that a state_machine callback is called at the correct point in a record's lifecycle is a little tricky because it isn't a regular callback chain you can invoke with run_callbacks
. Besides writing a real record to the database (which you shouldn't do for performance reasons), there are several ways to work around this.
1. Use it_should_receive_callbacks
The awesome it_should_run_callbacks
macro from spec_candy has been upgraded to work with state_machine callbacks:
describe Spaceship do
describe '#launch from :docked to :en_route' do
it_should_run_callbacks(:release_docking_clamps)
end
describe '#release_docking_clamps' do
# test clamp mechanism here
end
end
This does not currently distinguish between before_transition
and after_transition
.
2. Instantiate a state_machine transition
You can obtain the state machine's callback chain and run that. This is what spec_candy wraps for you (method #1).
describe Spaceship do
describe '#launch' do
it 'should run the proper callbacks' do
subject.state = 'docked'
subject.should_receive :release_docking_clamps
transition = StateMachine::Transition.new(subject, Spaceship.state_machine, :launch, :docked, :en_route)
transition.run_callbacks
end
end
describe '#release_docking_clamps' do
# test clamp mechanism here
end
end
3. Use keep_invalid!
By using the keep_invalid!
helper from spec_candy you can directly invoke the transition and not worry about hitting the database:
describe Spaceship do
describe '#launch' do
it 'should run the proper callbacks' do
subject.state = 'docked'
subject.should_receive :release_docking_clamps
subject.keep_invalid!
subject.launch
end
end
describe '#release_docking_clamps' do
# test clamp mechanism here
end
end
This method might not work with after_transition
callbacks (please investigate and report back here).