Buster.JS overview

Buster.JS is a JavaScript test framework for node and browsers.

Installation

See getting started.

The config file

Buster.JS needs a config file, both for node.js tests and browser tests. Name the config file buster.js and put it in either test/ or spec/ in your project.

var config = module.exports;

config["My tests"] = {
    env: "browser",        // or "node"
    rootPath: "../",
    sources: [
        "lib/mylib.js", // Paths are relative to config file
        "lib/**/*.js"   // Glob patterns supported
    ],
    tests: [
        "test/*-test.js"
    ]
}

// Add more configuration groups as needed

Run tests for your config file with:

buster test
// Assumes test/buster.js or spec/buster.js

buster test --config anywhere/buster.js
// Can also specify config file

Test cases

Typically, you have one test case per file. Here's an example, in test/my-thing-test.js. Since the file name ends with -test.js, the config file above will load the test.

buster.testCase("My thing", {
    "has the foo and bar": function () {
        assert.equals("foo", "bar");
    },

    "states the obvious": function () {
        assert(true);
    }
})

Note that you are free to name your test cases whatever you want. They don't have to start with "test ..." or "should ...".

Examples

Buster.JS has pluggable "front-ends" to specifying examples/tests/whatever. Here's the other bundled way of doing it:

// Expose describe and it functions globally
buster.spec.expose();

describe("My thing", function () {
    it("has the foo and bar", function () {
        expect("foo").toEqual("bar");
    });

    it("states the obvious", function () {
        expect(true).toBeSameAs(true);;
    });
});

Browser testing

Buster.JS can automate browsers, JsTestDriver style. First, start the server.

spacer

Open the browsers you want to run tests in and click the capture button.

spacer

Buster.JS automatically runs the tests in all the captured browsers.

spacer

Static HTML based browser testing

Buster also has a static browser runner that runs tests by opening a web page in a browser. This is similar to QUnit, Mocha, etc.

spacer

spacer

Node testing

Works just like browser tests, but you need to require Buster.JS in your tests.

var buster = require("buster");
var myLib = require("../lib/my-lib");

buster.testCase("A test case", {
    "test it": function () {
        assert(myLib.doIt());
    }
})

You can now run the file simply by doing `node my-test.js`, or you can create a configuration file with env: "node" that will run all tests in your project.

Use buster test in a terminal to initiate the test run. Here's the test output for buster-args.

spacer

Assertions

Buster.JS comes packed with assertions, and a simple DSL to add app-specific custom assertions.

assert(true);
assert.same(two, objects);
assert.equals(two, objects);
assert.defined(something);
assert.exception(function () { ... });
assert.isNull();
// .. and many more

Note the lack of assert.notEquals, assert.notDefined etc. Instead, Buster.JS provides a more symmetric API.

// This assertion does not exist!
assert.notEquals(foo, bar);

// Instead:
refute.equals(two, objects);

// And so on
refute(false);
refute.same(two, objects);
refute.defined(something);

So instead of changing the function name, replace assert with refute.

assert.match is neat. All the assertions below will pass.

// Partial property matching
var largeObject = {foo: "bar", baz: {test: "it"}};
assert.match(largeObject, {foo: "bar"});

// Fancy string matching
assert.match("Yeah!", { toString: function () { return "yeah"; } });

// Regexp matching
assert.match("Give me something", /^[a-z\s]$/i);

// Lazy types
assert.match("123", 123);

// DOM elements
var el = document.getElementById("myEl");

assert.match(el, {
    tagName: "h2",
    className: "item",
    innerHTML: "Howdy"
});

Adding your own custom assertions is easy. The DSL produces both an assert and refute. If you provide an expectation name, an expectation is created, too:

buster.assertions.add("inRange", {
    assert: function (num, lower, upper) {
        return num >= lower && num 

BDD syntax

Buster.JS is pluggable so you can write your own front-ends. Buster.JS also ships with two built-in front-ends; the xUnit style test cases we saw previously, and BDD style specs/examples.

buster.spec.expose(); // Make spec functions global

describe("Bowling kata", function () {
    before(function () {
        this.game = new BowlingGame();

        this.rollMany = function (rolls, pins) {
            for (var i = 0; i 

Reporters

There are a number of reporters built into Buster.JS. There is also a simple API for building your own reporters.

The default reporter is dots:

spacer

Other reporters:

spacer

spacer

spacer

spacer

All human-consumable reporters (i.e. not xml and tap output) can use no colors, bright colors or dim colors.

Deferred/pending tests

Commenting out an entire test case is bad. It will leave the test case out of the loop entirely, and you might forget to comment it back in again before pushing your code.

To remedy this, Buster.JS supports deferring a test so it doesn't actually run, but you get notified that there's a deferred tests every time you run your test suite.

spacer

To defer a test, add // to the start of the test name.

buster.testCase("My tests", {
    "// bla bla bla test case": function () {
        // This function will not be called
    },

    "this one is not deferred and will run": function () {
        assert(true);
    },

    "// exhibits feature A": "A simple place-holder, we need to detail this test"
})

Mocking and stubbing

Buster.JS ships with Sinon.JS. Every test in a test case has a sandbox associated with it, making it easy to mock and stub without worrying about side-effects beyond the scope of the test. assert also comes with lots of Sinon.JS-aware assertions.

buster.testCase("My tests", {
    "demonstrates stubbing": function () {
        this.stub(myLib.thingie, "aMethod"); // Will be automatically reverted
                                             // after the test completes
        doSomething();
        assert.calledOnce(myLib.thingie.aMethod);
    }
})

See full docs at sinon-buster.

Asynchronous tests

Asynchronous tests are tests that aren't finished running when the test method has finished executing. To tag a test as async, have the test function take one argument, done.

buster.testCase("My thing", {
    "test not asynchronous": function () {
        assert(true);
    },

    "test asynchronous": function (done) {
        myLibrary.doAjaxRequest("/foo", function (response) {
            assert.equals(response.statusCode, 200);
            done();
        });
    }
})

The done argument is a function. Call it to tell Buster.JS that the asynchronous test has finished running. If you don't call done, the test will eventually time out and fail. You can also have the test function return a thenable promise to make it asynchronous.

setUp and tearDown can also be asynchronous. The procedure is identical to that of tests.

buster.testCase("My thing", {
    setUp: function (done) {
        this.httpServer = http.createServer(function (req, res) {
            res.writeHead(418);
            res.end();
        });
        this.httpServer.listen(17171, function () { done(); });
        this.myThing = new MyThing();
        this.myThing.attach(this.httpServer);
    },

    tearDown: function (done) {
        this.httpServer.on("close", function () { done(); });
        this.httpServer.close();
    },

    // ... tests
})

Test case contexts

A test case can have nested contexts, as deep as you want. Pass an object instead of a function to create a context. Nested contexts can have their own setUp and tearDown methods.

buster.testCase("My thing", {
    setUp: function () {
        this.myThing = new MyThing();
    },

    "simple test": function () {
        assert(true);
    },

    "on steroids": {
        setUp: function () {
            this.myThing.onSteroids = true;
        },

        // ... tests

        "with cowbell": {
            setUp: function () {
                this.myThing.cobwell = true;
            },

            // ... tests
        }
    }
})

setUp is called top-down, so when a test in the context "with cowbell" is called, the root setUp is called, then the one in "on steroids", then lastly the one in "with cowbell". The this is the same in all contexts.

Proxying to http servers

In your browser tests you might want to perform HTTP request to a server, such as your application server. This can be difficult since your tests run via the Buster.JS server, and you can't access your application server due to cross domain origin policies in browsers.

To remedy this, Buster.JS lets you set up a proxy server in your config file.

var config = module.exports;

config["My tests"] = {
    sources: ["../lib/**/*.js"],
    tests: ["*-test.js"],
    resources: {
        "/app": "192.168.1.200:3030"
    }
}

A request to /app/foo will be proxied to 192.168.1.200:3030/foo.

If you're talking to an app server with state, you probably want to reset it before every test to avoid leaks from test case to test case. You're responsible for doing that yourself. Here's an example using an asynchronous setUp that won't run the test until the request to reset the app server has ended.

buster.testCase("My tests", {
    setUp: function (done) {
        myHttpLib("/app/reset", {
            success: function () { done(); }
        });
    },

    // ... tests here ...
})

Running a subset of tests

To run a single test, pass it's full name as an operand to buster test.

buster test "My tests should run this particular test"

The operand is treated as a JavaScript regular expression so you can do partial matching and regex stuff in it as well.

buster test "delete user"

If you don't quote the operand, it will be treated as a series of OR'd filters

To run a single file, do this:

buster test --file test/mytest.js

This assumes the presence of a config file, and just like plain buster test it tries to find a config file automatically, if you don't specify one with --config. Buster needs the config file to load your proxies, library code, dependencies, and so on.

Testing AJAX

Buster.JS comes with Sinon.JS. This makes mocking out the entire XHR stack in a browser trivial.

buster.testCase("My tests", {
    setUp: function () {
        this.server = this.fakeServer.create();
    },

    "should POST to /todo-items": function () {
        myThing.createTodoItem("Some item");

        assert.equals(this.server.requests.length, 1);
        assert.match(this.server.requests[0], {
            method: "POST",
            url: "/todo-items"
        });
    },

    "should yield list item to callback on success": function () {
        this.server.respondsWith(
            "POST",
            "/todo-items",
            [200, {"content-type": "application/json"},
            '{"text":"Fetch eggs","done":false,"id":1}']);

        var callback = this.spy();
        // Assuming implementation calls the callback with a JSON.parsed
        // response body when the request ends
        myThing.createTodoItem("Fetch eggs", callback);

        // Cause the request to respond, based on respondsWith above.
        this.server.respond();

        // Sinon.JS replaces the entire XHR stack and synchronously handles
        // the request.
        assert.calledOnce(callback);
        assert.equals(callback.getCall(0).args[0], {
            test: "Fetch eggs", done: false, id: 1
        });
    }
})

Sinon.JS mocks out the underlying XMLHttpRequest (or ActiveXObject) object, so your HTTP libraries don't need any modification to be testable in this way - even when using jQuery or another 3rd party library for your HTTP connections.

Feature detection

You can tell Buster.JS to not run certain test cases in certain situations. This is useful if you want to run the same test suite for a program that works in IE6, so you want to run most of your tests in IE6, but also has features that will crash when called in IE6.

buster.testCase("My thing", {
    requiresSupportFor: {
        "touch events": typeof(document.body.ontouchstart) != "object",
        "XHR": typeof(XMLHttpRequest) != "undefined"
    },

    "should receive touch events": function () {
        // ..
    },

    // ...
});

You can also apply the feature detection filter to nested contexts to only filter out a subset of the test case.

Custom test beds

Note: this feature has not yet landed in the beta. Currently it can sort of be achieved by adding resources: [{path: "/", content: "html here"}] to the config file.

For browser tests, you can specify the HTML document the tests will run in. Buster.JS defaults to a plain HTML5 document. But you might want to run the tests in a HTML4 strict environment, and what not.

var config = module.exports;

config["My tests"] = {
    sources: ["../lib/**/*.js"],
    tests: ["*-test.js"],
    testbed: "my-file.html"
}

Script tags for your tests will be added automatically at the ending body tag, or at the end of the document if no ending body tag is present.

Headless browser testing

Note: this feature has not yet landed in the beta.

You don't need a browser to do browser testing with Buster.JS. By running tests and not starting a server, Buster.JS will automatically run the tests headless in a PhantomJS browser.

This is particularly convenient for integration of Buster.JS with editors and IDEs. You can provide a simple "play button" to run the tests, and you don't need to do anything other than shelling out to buster test which will take care of running the tests in PhantomJS even if there's no Buster.JS server running.

Logging

Logging with buster.log will group the log messages in the reporter output with the test that was logged from. When logging objects of various sorts, the logger uses a (pluggable) formatter for pretty output.

spacer

In node.js, when running tests buster.log is available globally by default, for convenience. So you can buster.log in your implementations without requiring buster first.

Modularity

Buster.JS consists of many stand-alone modules with a documented API that can be re-used for various purposes.

The buster-assertions package can easily be used in other testing frameworks. If you use JsTestDriver, follow these steps (hint: it's pretty easy).

If you write your own testing framework, you may find many of our modules useful. buster-assertions is one such module, and is completely reusable. You can also use buster-capture-server if you want browser automation in your test framework, without implementing the actual browser automation part yourself.

Another example of usage of Buster.JS modules in other projects is Slidebuster (note: proof of concept). The buster-capture-server module is not test runner specific, it is a generic browser automation framework. Slidebuster uses it so that if you "capture" a normal browser and a touch device, you can swipe left and right on the touch device to change the slides on the normal browser.

AMD - control when tests start running

Some applications use a module loader (AMD = Asynchronous Module Definitions). So the default strategy of Buster.JS to start running tests on window.onload may not work for you. You can disable auto running and tell Buster.JS when to start running tests.

Add {autoRun: false} to your config file and call buster.run() to start the test run. That gives you full control over when the test run starts.

Full documentation here.

Global variables

By default, Buster.JS exposes four global variables: buster, expect, assert and refute. The two latter are also available as properties on the buster object (buster.assert, buster.refute). If you're a purist like us, you'll want to disable these additional globals and only have it expose the buster global variable (in browsers, on Node you'll have to require the things you want to use).

Note: in the beta, there's not yet a setting for disabling the exposure of these global variables

Editor integration

TextMate

Magnar Sveen maintains TextMate bundle. It includes snippets, running tests with command + R, and more.

Emacs

Christian Johansen maintains buster-mode.el.

Magnar Sveen has written a set of yasnippet snippets for Buster.JS.

Buster.JS Academy

Short, to-the-point screencasts about Buster.JS and unit testing in JavaScript. Watch.

gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.