Test setTimeout with Jasmine
Code that utilizes
setInterval becomes asynchronous. Jasmine has some nice helpers to make your test synchronous again.
Code Using setTimeout
setInterval respectively. There are also libraries that create some niceties for working in these situations, notably underscore’s
_.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
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.
Boom. Another testing hurdle cleared. Sometimes it’s harder to write the test than the code.