EventMachine's asynchronous and evented nature can be pretty tough to test. Here are some simple Test::Unit helpers I use along with a sample example:
def eventmachine(timeout = 1)
Timeout::timeout(timeout) do
EM.run do
EM.epoll
yield
end
end
rescue Timeout::Error
flunk 'Eventmachine was not stopped before the timeout expired'
end
This is a helper that runs eventmachine in a timeout so that if it hangs the test suite flunks out after a second. Very handy.
def set_em_steps(*steps)
@@_em_steps = *steps
end
def em_step_complete(step)
@@_em_steps.delete(step)
EM.stop if @@_em_steps.empty?
end
This is a flow-control helper to make sure I complete all the steps I expected. Sometimes you run two chunks of EM code and then make assertions in the callbacks. Generally, you call EM.stop in your last callback, but what if they don't chain one after another? Then you have to call stop after both have finished. These two helpers just make it so that I can define my steps, then mark each as completed. They stop EM once all the steps are completed.
Example
Here is an example test from Shortmail.com's test suite for iPhone push notifications:
test 'send a push notification to the push daemon' do
token = Factory.next :iphone_device_token
message = 'hello world'
# define two steps that must be completed before stopping
set_em_steps :payload, :notification
# run the following code, but time out after 1 second
eventmachine do
# MockServer is a fake iphone push server (pretending to be apple)
# it yields responses back to the instantiator
Test::Helpers::PushD::MockServer.listen do |response|
# Unpack the push
id, exp, device, payload = PushD::Pusher.unpack(response)
# make sure that the payload has the right into in it
assert_equal token, device
assert_equal message, JSON.parse(payload)['aps']['alert']
# mark the payload step as complete, meaning we've received
# and verified it
em_step_complete :payload
end
PushD::WebServer.listen
# This is another part of code under test.
# This is how we send a push message
PushNotifier.notify(token, :message => message) do |success|
# When the push message is sent it runs the callback block with
# a boolean and we ensure it's ok
assert success
# now make sure that this step is marked as finished
em_step_complete :notification
end
end
end
Now, we can be sure our assertions are run, or it will time out because it won't stop.
How do you test eventmachine?