How to Sleep in Tests
drbrain |
And Use Other Kernel Methods
Ruby provides many methods for you in Kernel including such favorites as #sleep, #fork, #open, #system and #` that can be hard to test reliably and quickly.
When you write code that uses these methods you want your tests to be reliable and fast but not need to set up too much state, like having files to open, before running (most importantly because that's more typing, but also because it's more stuff that may break). You also don't want the tests to be slow so actually sleeping or running a command are undesirable.
Let's start with some sample code. Imagine you've got a severe case of NIH and you're implementing cron (poorly):
def run command, every
loop do
status = system command
raise "#{command} failed" unless status
sleep every
end
end
And here's a test:
def test_run
e = assert_raises RuntimeError do
@cron.run 'false', 0
end
assert_equal 'false failed', e.message
end
How do we test the sleep behavior though? We'd need a command that failed on the second invocation. We'd also want to check the sleep duration somehow which would involve waiting.
There's another way though, since both #system and #sleep are in Kernel we can avoid calling the real implementations through the power of inheritance! If we have our tests inject an implementation of #sleep and #system into the implementation so the real methods never get called:
def test_run
def @cron.sleep time
@sleep = time
end
def @cron.system command
@commands ||= []
@commands << command
@commands.length <= 1
end
e = assert_raises RuntimeError do
@cron.run 'any old command', 2**30
end
assert_equal 'any old command failed', e.message
assert_equal 2**30, @cron.instance_variable_get(:@sleep)
assert_equal ['any old command', 'any old command'],
@cron.instance_variable_get(:@commands)
end
This test never calls Kernel#sleep nor Kernel#system, problem solved! As an additional benefit, this test will work on windows where the first will not because false may not exist.
Of course, you can always use a mocking or stubbing framework, but ruby is powerful enough on its own that you don't really need one. comments
Comments are disabled


