Promise-Returning Tests for Mocha
Or even better, if you are using Chai as Promised,
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
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
(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
npm install mocha-as-promised --save-dev to get up and running. Then:
You can of course put this code in a common test fixture file; for an example, see the Mocha as Promised tests themselves.
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-as-promised", you can use them as follows:
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:
Node, the Advanced Version
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
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.