mocha-as-promised

Mocha 1.18.0 and up now supports promises natively; use that instead!

Adds “thenable” promise support to the Mocha test runner.

npm install mocha-as-promised
111 downloads in the last day
1 221 downloads in the last week
6 851 downloads in the last month

Promises/A+ logo

Promise-Returning Tests for Mocha

So you really like Mocha. But you also really like promises. And you'd like to see support in Mocha for the promise-returning test style found in Buster and others, i.e. stuff like

it("should be fulfilled with 5", function () {
    return promise.then(function (result) {
        return result.should.equal(5);
    });
});

Or even better, if you are using Chai as Promised,

it("should be fulfilled with 5", function () {
    return promise.should.become(5);
});

Until now you've been making do with hacks, or perhaps using my fork of Mocha, and hoping I rebase often enough to keep things nice. But now, with Mocha as Promised, you have a much nicer option available!

How to Use

Once you install and set up Mocha as Promised, you now have a second way of creating asynchronous tests, besides Mocha's usual done-callback style. Just return a promise: if it is fulfilled, the test passes, and if it is rejected, the test fails, with the rejection reason as the error. Nice, huh?

If you want to do multiple assertions in a single test, first, think carefully about whether you should instead break that test up into multiple tests. Once you've decided that yes, you're really OK with multiple assertions, then you'll want to use a promise-aggregator function, like Q's Q.all:

it("should be fulfilled with an object with the correct properties", function () {
    var userPromise = getUserAsynchronously();

    return Q.all([
        userPromise.should.eventually.be.an("object"),
        userPromise.should.eventually.have.property("id", 123),
        userPromise.should.eventually.have.property("firstName", "Domenic"),
        userPromise.should.eventually.have.property("lastName", "Denicola")
    ]);
});

(Once again I'll plug my Chai as Promised library, so you can do super-awesome “eventual” assertions like these.)

Moch as Promised works with all Mocha interfaces: BDD, TDD, QUnit, whatever. It hooks in at such a low level, the interfaces don't even get involved.

Installation and Usage

Node

Do an npm install mocha-as-promised --save-dev to get up and running. Then:

require("mocha-as-promised")();

You can of course put this code in a common test fixture file; for an example, see the Mocha as Promised tests themselves.

AMD

Mocha as Promised supports being used as an AMD module, registering itself anonymously. So, assuming you have configured your loader to map the Mocha and Mocha as Promised files to the respective module IDs "mocha" and "mocha-as-promised", you can use them as follows:

define(function (require, exports, module) {
    var mocha = require("mocha");
    var mochaAsPromised = require("mocha-as-promised");

    mochaAsPromised(mocha);
});

<script> tag

If you include Mocha as Promised directly with a <script> tag, after the one for Mocha itself, then it will automatically plug in to Mocha and be ready for use:

<script src="mocha.js"></script>
<script src="mocha-as-promised.js"></script>

Node, the Advanced Version

The require("mocha-as-promised")() above tries to detect which instance of Mocha is being used automatically. This way, Mocha as Promised can plug into either the local Mocha instance installed into your project, or into the global Mocha instance if you're running your tests using the globally-installed command-line runner.

In some cases, if you're doing something weird, this can fall down. In these cases, you can pass an array of Mocha instances into the Mocha as Promised function. For example, if you somehow had your Mocha module as a property of the foo module, instead of it being found in the usual npm directory structures, you would do

require("mocha-as-promised")([require("foo").MyMocha]);

How Does This Work!?

Black magic. No, seriously, this is a big hack.

The essential strategy is to intercept any test functions that Mocha runs, and inspect their return values. If they return a promise, then translate fulfillment/rejection appropriately. It's explained in more detail how exactly this is done in a large comment block at the top of the source. You can also check out d98f2d9 for an alternative approach at the interception, which was abandoned in favor of the current one.

Note that Mocha as Promised doesn't just override Runnable.prototype.run, as is done by my Mocha fork. That seemed a bit too fragile to be a long-term solution. The interception approach involves more black magic, but is probably more resilient in the face of upstream changes. At least, that's the hope.

npm loves you