What it sorts down into is roughly three parts: the protocol, browserify, and magic.
TAP, or the Test Anything Protocol is the definition of ‘tried & true’: it’s been around since 1987 and has been implemented in a ton of languages. It’s just a dead-simple way to format test results, like
Having this format be super simple means that you can combine tools that have common expectations. For instance, faucet is a node module that gives pretty, summarized results for your tests, but it doesn’t directly plug into
node-tap - all it cares about is the TAP protocol, so you can pipe results into it. You could even pipe a TAP producer in a different language into it, and it’ll work just the same.
browserify is another awesome tool written by substack. With it, you can use node-style
require() calls in code you’re going to run in browsers, and then use the
browserify command to stitch them all together and make something you can throw in a script tag.
Browserify has been huge for writing cross-platform libraries, and it’s been huge for building things. Mapbox.com and Mapbox.js are both constructed this way - individual libraries on npm,
package.json just like nodejs code, and then browserify to bring it to the web.
Long story short,
browserify ‘just works’ with
mocha has something of an unusual browser story - it’s hard to get at the files you’d want to run tests in a browser, and then when you do, if you want to run tests in both browsers and node, it’s not straightforward to conditionally require things sometimes.
So, to run browserify tests in a browser, you can just run
console.log to write its results, which is super easy to pull out of a headless browser.
So, we’ve discussed TAP a little bit, and you might notice that mocha supports TAP too. So why not just use mocha to write tests? Let’s talk about that.
Mocha does a little magic. With only few exceptions, nodejs has the assumption that any variables on a page will come from obvious places:
In the interest of simplicity,
mocha doesn’t follow this rule. Your mocha test files have assumptions:
Keen eyes will notice that
assert entered the stage by a
require() call, but
it didn’t - they appear magically.
On the other hand, a basic
test comes from
t comes from the closure. Simple enough. This lack of abstraction has two awesome advantages:
tand then call functions off of
t? It’ll work.
node test/test.js, and it works. As opposed to needing a ‘test runner’ binary that contains some of the code the test really needs.
Continuous Integration, where every commit to GitHub is automatically tested, has become a necessity. Setting the green ‘this works’ badge on projects means something, and we’ve found that running tests on remote hosts can give a better sanity check than just running them locally - the environment is constructed from scratch, there aren’t any stray files that make things work.
tape works with testling just like mocha - the same minimal
.travis.yml file ‘just works’ as long as
npm install and
npm test do their things. But on top of that, you can use Testling-CI and testling, and test commits in browsers. Testling-CI works the same as Travis: set a webhook, tweak a few package.json properties, and you’ll get a page of your own with results. testling, on the other hand, runs headless tests locally, with your real browsers, not with phantomjs or another custom abstraction. In combination, this means that you can easily cross-browser test code. And if the abstractions break, it’s easy to just
browserify the code and run the tests manually.