buster.assertions

Version
0.8.0 (2011-11-28)
Module
require("buster-assertions");
In browsers
buster.assertions;

A collection of assertions to be used with a unit testing framework. buster-assertions works well with any CommonJS compliant testing framework out of the box, and can easily be configured to work with most any testing framework.

buster-assertions contains lots of assertions. We strongly believe that high-level assertions are essential in the interest of producing clear and intent-revealing tests, and they also give you to-the-point failure messages.

Overriding assertion messages

The default assertion messages can be overridden per assertion. The properties to overwrite are listed with each assertion along with the arguments the string is fed. Here's an example of providing a new assertion failure message for buster.assertions.assert.equals:

var assert = buster.assertions.assert;
assert.equals.msgFail = "I wanted ${0} == ${1}!"
try {
    assert.equals(3, 4);
} catch (e) {
    console.log(e.message);
}
// Prints:
// "I wanted 3 == 4!"

Note that Buster.JS assertions do not allow for custom messages. If an assertion is not failing with a specific enough message, provide your own domain-specific add.

Assertions and refutations

Unlike most assertion libraries, buster-assertion does not have assert.notXyz assertions to refute some fact. Instead, it has refutations, heavily inspired by Ruby's minitest:

var assert = buster.assertions.assert;
var refute = buster.assertions.refute;
assert.equals(42, 42);
refute.equals(42, 43);

Refutations help express "assert not ..." style verification in a much clearer way. It also brings with it a nice consistency in that any assert.xyz always has a corresponding refute.xyz that does the opposite check.

Adding custom assertions

Custom, project specific assertions can help the readability of your tests, and improve your test feedback. You can add assertions that behave exactly like the built-in ones (i.e. with counting, message formatting, expectations and more) by using the add method.

Events

buster.assertions is an buster-eventEmitter. Listen to events with on:

buster.assertions.on("failure", function (err) {
                console.log(err.message);
                });

Event: "pass", function () {}

Assertion passed. The callback is invoked with the assertion name, e.g. "isString", as its only argument. Note that this event is also emitted when refutations pass.

Event: "failure", function (error) {}

Assertion failed. The callback is invoked with an AssertionError object.

Assertions

Examples assume that you have aliased buster.assertions.assert as such:

var assert = buster.assertions.assert;

assert(actual[, message]);

Fails if actual is falsy (0, "", null, undefined, NaN). Fails with either the provided message or "Expected null to be truthy". This behavior differs from most other assertions, which prepend the failure message with the optional message argument.

assert({ not: "Falsy" }, "This will pass");
assert(null, "This will fail");
assert(null); // Fails
assert(34);   // Passes

assert.same(expected, actual)

Fails if actual is not the same object (===) as expected. To compare similar objects, such as { name: "Chris", id: 42 } and { id: 42, name: "Chris" } (not the same instance), see assert.equals. The optional message is prepended to the failure message if provided.

var obj = { id: 42, name: "Chris" };
assert.same(obj, obj, "This will pass");
assert.same(obj, { id: 42, name: "Chris" }, "This will fail");
assert.same(obj, obj);                       // Passes
assert.same(obj, { id: 42, name: "Chris" }); // Fails

Message

assert.same.msgFail = "Expected ${0} to be the same object as ${1}";

${0}
The actual object
${1}
The expected object

assert.equals(actual, expected)

Compares actual to expected property for property. If the property count does not match, or if any of actual's properties does not match the corresponding property in expected the assertion fails.

If actual is null or undefined, an exact match is required. Date objects are compared by their getTime method. Regular expressions are compare by their string representations. Primitives are compared using ==, i.e., with coercion.

equals passes when comparing an arguments object to an array if the both contain the same elements.

The optional message is prepended to the failure message if provided.

var assert = assert;
assert.equals({ name: "Professor Chaos" }, { name: "Professor Chaos" }, "Passes");
assert.equals({ name: "Professor Chaos" }, { name: "Dr Evil" }, "Fails");
assert.equals({ name: "Professor Chaos" }, { name: "Professor Chaos" }); // Passes
assert.equals({ name: "Professor Chaos" }, { name: "Dr Evil" });         // Fails

Message

assert.equals.msgFail = "Expected ${0} to be equal to ${1}";

${0}
The actual object
${1}
The expected object

assert.typeOf(object, expected)

Fails if typeof object is not expected. The optional message is prepended to the failure message if provided.

assert.typeOf({}, "object", "This will pass");
assert.typeOf(null, "function", "This will fail");
assert.typeOf({}, "object");     // Passes
assert.typeOf(null, "function"); // Fails

Message

assert.typeOf.msgFail = "Expected typeof ${0} (${2}) to be ${1}";

${0}
The actual object
${1}
The expected type, a string
${2}
typeof object

assert.isObject(object)

Fails if object is not an object or if it is null. The optional message is prepended to the failure message if provided.

assert.isObject({}, "This will pass");
assert.isObject(42, "This will fail");
assert.isObject([1, 2, 3]);      // Passes
assert.isObject(function () {}); // Fails

Message

assert.isObject.msgFail = "Expected typeof ${0} (${1}) to be object";

${0}
The actual object
${1}
typeof object

assert.isNumber(object)

Fails if object is not a number or if it's NaN. NaN is rarely an expected value, even when expecting numbers. Use isNaN to check explicitly for NaN, or in those cases where you actually expect any number (including NaN), use assert.typeOf. The optional message is prepended to the failure message if provided.

assert.isNumber(42, "This will pass");
assert.isNumber({}, "This will fail");
assert.isNumber(1423); // Passes
assert.isNumber(NaN);  // Fails

Message

assert.isNumber.msgFail = "Expected ${0} (${1}) to be a non-NaN number";

${0}
The actual object
${1}
typeof object

assert.defined(object)

Fails if object is undefined. The optional message is prepended to the failure message if provided.

var a;
assert.defined({}, "This will pass");
assert.defined(undefined, "This will fail");
assert.defined({});  // Passes
assert.defined(a); // Fails

Message

assert.defined.msgFail = "Expected to be defined";

assert.isNull(object)

Fails if object is not null. The optional message is prepended to the failure message if provided.

assert.isNull(null, "This will pass");
assert.isNull({}, "This will fail");
assert.isNull(null); // Passes
assert.isNull({});   // Fails

Message

assert.isNull.msgFail = "Expected ${0} to be null";

${0}
The actual object

assert.match(actual, matcher)

Fails if matcher is not a partial match about actual. Accepts a wide range of input combinations.

String matcher

In its simplest form, assert.match performs a case insensitive substring match. When the matcher is a string, the actual object is converted to a string, and the assertion passes if actual is a case-insensitive substring of expected as a string.

Boolean matcher

Performs a strict (i.e. ===) match with the object.

assert.match("Give me something", "Give", "This will pass");
assert.match("Give me something", "sumptn", "This will fail");
assert.match("Yeah!", { toString: function () { return "yeah"; } }); // Passes

Regular expression matcher

When the matcher is a regular expression, the assertion will pass if expected.test(actual) is true. assert.match is written in a generic way, so any object with a test method will be used as a matcher this way.

assert.match("Give me something", /^[a-z\s]$/i, "This will pass");
assert.match("Give me something", /[0-9]/, "This will fail");
assert.match({ toString: function () { return "yeah!"; } }, /yeah/); // Passes
assert.match(234, /[a-z]/); // Fails

Number matcher

When the matcher is a number, the assertion will pass if matcher == actual.

assert.match("123", 123, "This will pass");
assert.match("Give me something", 425, "This will fail");
assert.match({ toString: function () { return "42"; } }, 42); // Passes
assert.match(234, 1234); // Fails

Function matcher

When the matcher is a function, it is called with actual as its only argument. The assertion will pass if the function returns true. A strict match is performed against the return value, so a boolean true is required, truthy is not enough.

assert.match("123", function (exp) {
    return exp == "123";
}, "This will pass");
assert.match("Give me something", function () {
    return "ok";
}, "This will fail");
assert.match({
    toString: function () {
        return "42";
    }
}, function () { return true; }); // Passes
assert.match(234, function () {}); // Fails

Object matcher

As mentioned above, if an object matcher defines a test method the assertion will pass if matcher.test(actual) returns truthy. If the object does not have a test method, a recursive match is performed. If all properties of matcher matches corresponding properties in actual, the assertion passes.

assert.match("123", {
    test: function (arg) {
        return arg == 123;
    }
}, "This will pass");
assert.match({}, { prop: 42 }, "This will fail");
assert.match({
    name: "Chris",
    profession: "Programmer"
}, {
    name: "Chris"
}); // Passes
assert.match(234, {
    name: "Chris"
}); // Fails

DOM elements

assert.match can be very helpful when asserting on DOM elements, because it allows you to compare several properties with one assertion:

var el = document.getElementById("myEl");
assert.match(el, {
    tagName: "h2",
    className: "item",
    innerHTML: "Howdy"
});

Messages

assert.match.msgException = "${1}";

Used when the matcher function throws an exception. This happens if the matcher is not any of the accepted types, for instance, a boolean.

${0}
Message from exception thrown by matcher function.

assert.match.msgFail = "Expected ${0} to match ${1}";

${0}
The actual object
${1}
The expected object

assert.exception(callback[, type])

Fails if callback does not throw an exception. If the optional type is provided, the assertion fails if the callback either does not throw an exception, or if the exception is not of the given type (determined by its name property). The optional message is prepended to the failure message if provided.

assert.exception(function () {
    throw new Error("Ooops!");
}, null, "This will pass");
assert.exception(function () {}, null, "This will fail");
assert.exception(function () {
    throw new Error("Ooops!");
}); // Passes
assert.exception(function () {}); // Fails
assert.exception(function () {
    throw new TypeError("Ooops!");
}, "TypeError", "This will pass");
assert.exception(function () {
    throw new Error("Aww");
}, "TypeError", "This will fail, wrong exception type");
assert.exception(function () {
    throw new Error("Ooops!");
}, "Error"); // Passes
assert.exception(function () {}, "TypeError"); // Fails

Messages

assert.exception.msgTypeNoException = "Expected ${0} but no exception was thrown";
assert.exception.msgFail = "Expected exception";
assert.exception.msgTypeFail = "Expected ${0} but threw ${1}, (${2})";

${0}
The expected exception type (if provided)
${1}
The type of exception thrown (if any)
${2}
The exception message

Browser/DOM-specific assertions

assert.tagName(element, tagName)

Fails if the element either does not specify a tagName property, or if its value is not a case-insensitive match with the expected tagName. The optional message is prepended to the failure message if provided.

assert.tagName(document.createElement("p"), "p", "This will pass");
assert.tagName(document.createElement("p"), "li", "This will fail");
assert.tagName(document.createElement("h2"), "H2"); // Passes
assert.tagName(document.createElement("p"), "li");  // Fails

Message

assert.tagName.msgNoTagName = "Expected ${1} to have tagName property";

${0}
The expected tagName
${1}
If the object does not have a tagName property, this is the object. Otherwise, it is the value of object.tagName.

assert.className(element, className)

Fails if the element either does not specify a className property, or if its value is not a space-separated list of all class names in classNames.

classNames can be either a space-delimited string or an array of class names. Every class specified by classNames must be found in the object's className property for the assertion to pass, but order does not matter.

The optional message is prepended to the failure message if provided.

var el = document.createElement("p");
el.className = "feed item blog-post";
assert.className(el, "item", "This will pass");
assert.className(el, "news", "This will fail");
assert.className(el, "blog-post feed"); // Passes
assert.className(el, "feed items");     // Fails, "items" is not a match
assert.className(el, ["item", "feed"]); // Passes

Message

assert.className.msgNoClassName = "Expected object to have className property";

${0}
The expected classNames
${1}
The value of the object's className property, if any.

Refutations

Examples assume that you have aliased buster.assertions.refute as such:

var refute = buster.assertions.refute;

refute.same(expected, actual)

Fails if actual is the same object (===) as expected. To compare similar objects, such as { name: "Chris", id: 42 } and { id: 42, name: "Chris" } (not the same instance), see refute.equals. The optional message is prepended to the failure message if provided.

var obj = { id: 42, name: "Chris" };
refute.same(obj, { id: 42, name: "Chris" }, "This will pass");
refute.same(obj, obj, "This will fail");
refute.same(obj, { id: 42, name: "Chris" }); // Passes
refute.same(obj, obj);                       // Fails

Message

refute.same.msgFail = "Expected ${0} not to be the same object as ${1}";

${0}
The actual object
${1}
The expected object

refute.equals(actual, expected)

Passes in any case where assert.equals fails. The optional message is prepended to the failure message if provided.

var assert = assert;
refute.equals({ name: "Professor Chaos" }, { name: "Dr Evil" }, "Passes");
refute.equals({ name: "Professor Chaos" }, { name: "Professor Chaos" }, "Fails");
refute.equals({ name: "Professor Chaos" }, { name: "Dr Evil" });        // Passes
refute.equals({ name: "Professor Chaos" }, { name: "Professor Chaos" });// Fails

Message

refute.equals.msgFail = "Expected ${0} not to be equal to ${1}";

${0}
The actual object
${1}
The expected object

refute.typeOf(object, expected)

Fails if typeof object is expected. The optional message is prepended to the failure message if provided.

refute.typeOf(null, "function", "This will pass");
refute.typeOf({}, "object", "This will fail");
refute.typeOf(null, "function"); // Passes
refute.typeOf({}, "object");     // Fails

Message

refute.typeOf.msgFail = "Expected typeof ${0} not to be ${1}";

${0}
The actual object
${1}
The expected type, a string

refute.defined(object)

Fails if object is not undefined. The optional message is prepended to the failure message if provided.

var a;
refute.defined(undefined, "This will pass");
refute.defined({}, "This will fail");
refute.defined(a);  // Passes
refute.defined({}); // Fails

Message

refute.defined.msgFail = "Expected typeof ${0} (${1}) to be undefined";

${0}
The actual object
${1}
typeof object

refute.isNull(object)

Fails if object is null. The optional message is prepended to the failure message if provided.

refute.isNull({}, "This will pass");
refute.isNull(null, "This will fail");
refute.isNull({});   // Passes
refute.isNull(null); // Fails

Message

refute.isNull.msgFail = "Expected not to be null";

refute.match(actual, pattern)

Fails in cases where assert.match passes.

Messages

refute.match.msgException = "${0}";

Used when the matcher function throws an exception. This happens if the matcher is not any of the accepted types, for instance, a boolean.

${0}
Message from exception thrown by matcher function.

refute.match.msgFail = "Expected ${0} not to match ${1}";

${0}
The actual objetc
${1}
The expected object

refute.exception(callback)

Fails if callback throws an exception. The optional message is prepended to the failure message if provided.

refute.exception(function () {
    // Exercise code...
}, "This will pass");
refute.exception(function () {
    throw new Error("Ooops!");
}, "This will fail");
refute.exception(function () {
    // Exercise code...
}); // Passes
refute.exception(function () {
    throw new TypeError("Ooops!");
}); // Fails

Message

refute.exception.msgFail = "Expected not to throw but threw ${0}, (${1})";

${0}
The type of exception thrown (if any)
${1}
The exception message

DOM-specific refutations

refute.tagName(element, tagName)

Fails if the element either does not specify a tagName property, or if its value is a case-insensitive match with the expected tagName. The optional message is prepended to the failure message if provided.

refute.tagName(document.createElement("p"), "LI", "This will pass");
refute.tagName(document.createElement("p"), "p", "This will fail");
refute.tagName(document.createElement("h2"), "H3"); // Passes
refute.tagName(document.createElement("p"), "p");   // Fails

Message

refute.tagName.msgNoTagName = "Expected ${1} to have tagName property";

${0}
The expected tagName
${1}
If the object does not have a tagName property, this is the object.

refute.className(element, className)

Fails if the element either does not specify a className property, or if its value is a space-separated list of all class names in classNames.

classNames can be either a space-delimited string or an array of class names. If any class specified by classNames is not found in the object's className property the assertion passes. Order does not matter.

The optional message is prepended to the failure message if provided.

var el = document.createElement("p");
el.className = "feed item blog-post";
refute.className(el, "chicken", "This will pass");
refute.className(el, "news", "This will fail");
refute.className(el, "blog-post rss");  // Passes
refute.className(el, "feed item");      // Fails
refute.className(el, ["item", "feed"]); // Passes

Message

refute.className.msgNoClassName = "Expected object to have className property";

${0}
The expected classNames
${1}
The value of the object's className property, if any. Otherwise, the object itself.

Methods

fail(message)

When an assertion fails, it calls buster.assertions.fail with the failure message as the only argument. The built-in fail function both throws an AssertionError and emits it to the "failure" event. The error can be caught and handled by the test runner. If this behavior is not suitable for your testing framework of choice, you can override buster.assertions.fail to make it do the right thing.

Example: To use buster-assertions with JsTestDriver, you can simply configure it as follows:

buster.assertions.fail = function (message) {
    fail(message);
};

Where the global fail function is the one provided by JsTestDriver.

It is possible to make the default assert.fail method only emit an event and not throw an error. This may be suitable in asynchronous test runners, where you might not be able to catch exceptions. To silence exceptions, see the throwOnFailure property.

format(object)

Values inserted into assertion messages using the ${n} switches are formatted using buster.assertions.format(obj). By default this method simply coerces the object to a string.

A more expressive option is to use buster-format, which is a generic function for formatting objects nicely as ascii. For nice ascii formatting of objects (including DOM elements) do:

buster.assertions.format = buster.format.ascii;

add(name, options)

Adds a custom assertion. Using this 'macro' to add project specific assertions has a few advantages:

  • Assertions will be counted.
  • Failure messages will have interpolated arguments formatted by buster.assertions.format.
  • A single function generates both an assertion and a refutation.
  • If using expectations, an expectation can easily be generated as well.
  • When failOnNoAssertions is set to true, the assertion will behave correctly (important for asynchronous tests).
  • The assertion will fail if too few arguments are passed.

Here's an example of adding a "foo" validator, that only passes when its only argument is the string "foo":

var assert = buster.assertions.assert;
var refute = buster.assertions.refute;
var expect = buster.assertions.expect;
buster.assertions.add("isFoo", {
    assert: function (actual) {
        return actual == "foo";
    },
    assertMessage: "Expected ${0} to be foo!",
    refuteMessage: "Expected not to be foo!",
    expect: "toBeFoo"
});
// Now you can do:
// Passes
assert.isFoo("foo");
// Fails: "[assert.isFoo] Expected { id: 42 } to be foo!"
assert.isFoo({ id: 42 });
// Fails: "[assert.isFoo] Ouch: Expected { id: 42 } to be foo!"
assert.isFoo({ id: 42 }, "Ouch");
// Fails: "[refute.isFoo] Expected not to be foo!"
refute.isFoo("foo");
// Passes
expect("foo").toBeFoo();

Arguments

name

The name of the new assertion/refutation.

options
assert

The verification function. Should return true when the assertion passes. The generated refutation will pass when the function returns false.

In some cases the refutation may not be the exact opposite of the assertion. If that is the case you should provide code>options.refute for the custom refutation.

The number of formal parameters the function accepts determines the number of required arguments to the function. If the assertion is called with less arguments than expected, Buster will fail it before your custom function is even called.

All arguments are available for interpolation into the resulting error message. The first argument will be available as "${0}", the second as "${1}" and so on. If you want to embed other values than exact arguments into the string, you can set properties on this in the custom assertion, and refer to them as "${name}" in the message.

refute
Custom refutation function. Used over !assert() if provided.
assertMessage
The error message to use when the assertion fails. The message may refer to arguments through switches like "${0}" and so on (see above, under the assert argument).
refuteMessage
Like assertFail, but for refutations
values

A function that maps values to be interpolated into the failure messages. This can be used when you need something more/else than the actual arguments in order. For instance, the built-in assert.typeOf assertion needs the actual type. Its assertMessage is "Expected typeof ${0} (${2}) to be ${1}", and its values option is:

values: function (actual, expected) {
    return [actual, expected, typeof actual];
}
expect
The name of the assertion as an expectation, e.g. "toBeSomething". Optional.

Supporting utilities

isNode(object)

Returns true if the object is a DOM node. The check is made by attempting to call appendChild on it, passing in an element.

isElement(object)

Returns true if the object is a DOM element. The check is made by calling buster.isNode and asserting that the element's nodeType is 1 (i.e. element).

isArguments(object)

Returns true if the argument is an arguments object. Buster checks this by making sure the object is array-like, but not actually an array.

function check() {
              buster.isArguments(arguments); // true
              }
              buster.isArguments([]); // false

keys(object)

Cross-browser implementation of Object.keys. From MDC: returns an array whose elements are strings corresponding to the enumerable properties found directly upon object. The ordering of the properties is the same as that given by looping over the properties of the object manually.

Properties

count (0)

buster.assertions.count is incremented anytime an assertion is called. The assertion counter can be reset to any number at your convenience.

throwOnFailure (true)

When using the default fail implementation, this property can be set to false to make assertion failures not throw exceptions (i.e. only emit events). This may be suitable in asynchronous test runners, where you might not be able to catch exceptions.

Supporting objects

AssertionError

An exception (specifically, an Error object) whose name property is "AssertionError".