Unit testing promises in JavaScript does not have to be painful. Here is how to solve common pain points.
Too much boilerplate
Most of the boilerplate when testing promise-returning code comes from controlling the test runner. For example, when using qunit one needs to stop the test (or declare the test as async), then start the runner again:
1 | QUnit.start('promise test', function () { |
Notice that we have more lines stopping and starting QUnit test runner than actually testing the resolved value.
Resolved vs rejected promise
You should check if the promise that should be resolved is not rejected instead, again adding to the boilerplate
1 | QUnit.start('promise test', function () { |
Catch errors
The promises catch thrown errors hoping that somewhere there would be
an error handler ready to handle them. If you do not have an error handler
and do not close the chain using .done
any caught error silently disappears.
Thus you should always at least close the promise chain, adding to the boilerplate
1 | QUnit.start('promise test', function () { |
Solution 1: use plugin
Shameless self-promotion: I wrote a qunit-promises plugin to remove the boilerplate to stop / start the qunit runner and check resolved vs rejected expectations. Same example as above looks much simpler.
1 | QUnit.test('promise test', function (assert) { |
For Jasmine fans, there is jasmine-as-promised.
Solution 2: use promise-supporting test runner
If you use behavior-drive development (BDD), you can use mocha testing framework which has built-in promise support. Any unit test that returns a promise is automatically processed as async, including error checking or rejected promise checking. To check the resolved value, just attach the assertions to the original promise chain. In the example below I am using chai-as-promised assertion library.
1 | it('promise test', function(){ |
I prefer using expect.js assertion library instead for clarity
1 | it('promise test', function(){ |
Another interesting test runner with native promises support is vows.js.