Test setTimeout with Jasmine
Code that utilizes setTimeout
or setInterval
becomes asynchronous. Jasmine has some nice helpers to make your test synchronous again.
Code Using setTimeout
Any Javascript code that wants to delay certain actions or give intervals between actions will use setTimeout
or setInterval
respectively. There are also libraries that create some niceties for working in these situations, notably underscore's _.throttle
and _.debounce
functions. Underneath all of these higher-level functions, they're using the native setTimeout
construct, so the same strategies will apply. In your source, you end up with something like this:
$(document).on 'keyup', '.autocomplete-field', _.debounce(autocomplete, 500)
This code binds a keyup event listener on a contrived .autocomplete-field
. Of course, we don't want to do an expensive autocomplete
lookup on every keypress. So, we'll slow it down a bit with _.debounce
.
Testing Asynchronous Code in Jasmine
Now we go to test it in Jasmine (failing):
describe 'Autocomplete Field', ->
it 'calls autocomplete when typing', ->
called = false
autocomplete = ->
called = true
keyup = $.Event('keyup')
$('.autocomplete-field').trigger keyup
called.should.be.true
In this test, or autocomplete
spy will never be called, thus called
will never be true. There are a few ways to fix this.
Mocking the Jasmine Clock
One way to fix the test is to remove _.debounce
from our source code. That would make the test pass. The other is to setup our Jasmine to handle the setTimeout
used in _.debounce
. Let's do the latter, and raise our fists to the sky in triumph over our testing foe (winning):
describe 'Autocomplete Field', ->
beforeEach ->
jasmine.Clock.useMock()
it 'calls autocomplete when typing', ->
called = false
autocomplete = ->
called = true
keyup = $.Event('keyup')
$('.autocomplete-field').trigger keyup
jasmine.Clock.tick 501
called.should.be.true
The needed code is simple to add. Use jasmine.Clock.useMock()
to setup the fake ticker. Then call tick()
on it whenever you want your test to simulate time massage.
The cool thing is that this does not slow down your tests. You could set the clock tick to be 50100000 or some crazy number, and it will still execute as fast as your single JavaScript thread will let you.
Boom. Another testing hurdle cleared. Sometimes it's harder to write the test than the code.