The code
Let’s say you have a Node module (named potentialPartner.js) that returns a promise, as such:
var Q = require("q"); function willYouLoveMe(cond){ var deferred = Q.defer(); if (cond === "even if I were out of shape") deferred.reject("I only like guys in shape"); else deferred.resolve("I love you unconditionally!"); return deferred.promise; } module.exports = { willYouLoveMe: willYouLoveMe };
How can we test it?
To cover all scenarios, you’ll need a way to exercise all possible outcomes of the promise (in this case a fulfilled promise, and a rejected rejected promise). Using mocha, should, and (obviously) Q, here’s what the tests may look like:
var should = require("should"), Q = require("q"); describe("A potential partner", function() { var potentialPartner = require("../potentialPartner"); it("should promise to love me", function() { var promise = potentialPartner.willYouLoveMe(); promise.should.have.property("then"); promise.should.have.property("fail"); }); it("should promise to love me unconditionally", function(done) { var promise = potentialPartner.willYouLoveMe("no matter what"); promise.done(function(){ // onFulfilled done(); }, function() { // onRejected should.fail("I expect my partner to love me unconditionally!"); done(); }); }); it("should not promise to love me ONLY if I'm in shape", function(done) { var promise = potentialPartner.willYouLoveMe("even if I were out of shape"); promise.done(function(){ // onFulfilled should.fail("I expect my partner to not just promise me to love me if I'm in shape :("); done(); }, function() { // onRejected done(); }); }); });
Note that:
- The very first test (“should promise to love me”) is probably unnecessary. However, I like to have very specific tests that make the failure obvious when they fail. This makes them more effective, and the failures quicker to debug.
- I’m using mocha’s
done
function to handle the async results from the method under test. Otherwise, the async tests will complete before the assertions are executed, which will cause mocha to report false positives. - I’m using
promise.done()
to hook up my assertions. This is key! … you may feel inclined to chain athen
to your promise, or afail
(depending on the condition you want to test), but the main problem with that has to do with the way exceptions are handled internally bythen
/fail
. The bottom line is that such handling can cause issues with your failing assertions and the way they end up getting reported by the test framework (it can hamper debug-ability). Instead,promise.done()
plays more nicely with your test reports.