referee

A collection of assertions to be used with a unit testing framework. referee works well with any CommonJS compliant testing framework out of the box, and can easily be configured to work with most any testing framework. See also expectations if you like the alternative API (expect(thing).toBe*).

referee 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.

Install

npm install @sinonjs/referee --save-dev

Sinon.JS integration

You can extend referee with assertions that integrates with Sinon.JS

See referee-sinon

Assertions and refutations

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

var assert = referee.assert;
var refute = referee.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.

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 all other assertions, which prepend the optional message argument.

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

refute()

refute(actual[, message])

Fails if actual is truthy. Fails with either the provided message or "Expected null to be falsy". This behavior differs from all other refutations, which prepend the optional message argument.

refute({ not: "Falsy" }, "This will fail"); // Fails with custom message
refute(null, "This will pass");
refute(null); // Passes
refute(34); // Fails

Predefined Assertions

The following assertions can be used with assert and refute. They are described for assert, but the corresponding failure messages for refute are also mentioned. For refute the behaviour is exactly opposed.

All assertions support an optional message argument, which is prepended to the failure message.

Overview

Any

String

Number

Function

Object

Array and array like

DOM element

Promise

Types and values

These assertions are for checking for built-in types and values.

same()

assert.same(actual, expected[, message])

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 equals().

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

Messages

assert.same.message = "${actual} expected to be the same object as ${expected}";
refute.same.message =
  "${actual} expected not to be the same object as ${expected}";

equals()

assert.equals(actual, expected[, message])

Compares actual to expected property by property. If the property count does not match, or if any of actual‘s properties do not match the corresponding property in expected, the assertion fails. Object properties are verified recursively.

If actual is null or undefined, an exact match is required. Date objects are compared by their getTime method. Regular expressions are compared 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. Objects or arrays may contain the result of calling match to compare a property using a built-in or custom matcher.

assert.equals({ name: "Professor Chaos" }, { name: "Professor Chaos" }); // Passes
assert.equals({ name: "Professor Chaos" }, { name: match.string }); // Passes
assert.equals({ name: "Professor Chaos" }, { name: "Dr Evil" }); // Fails
assert.equals({ name: "Professor Chaos" }, { name: match.number }); // Fails

Messages

assert.equals.message = "${actual} expected to be equal to ${expected}";
refute.equals.message = "${actual} expected not to be equal to ${expected}";

greater()

assert.greater(actual, expected[, message])

Fails if actual is equal to or less than expected.

assert.greater(2, 1); // Passes
assert.greater(1, 1); // Fails
assert.greater(1, 2); // Fails

Messages

assert.greater.message = "Expected ${actual} to be greater than ${expected}";
refute.greater.message =
  "Expected ${actual} to be less than or equal to ${expected}";

less()

assert.less(actual, expected[, message])

Fails if actual is equal to or greater than expected.

assert.less(1, 2); // Passes
assert.less(1, 1); // Fails
assert.less(2, 1); // Fails

Messages

assert.less.message = "Expected ${actual} to be less than ${expected}";
refute.less.message =
  "Expected ${actual} to be greater than or equal to ${expected}";

isUndefined()

assert.isUndefined(object[, message])

Fails if object is not undefined.

assert.isUndefined(undefined); // Passes
assert.isUndefined({}); // Fails
refute.isUndefined({}); // Passes
refute.isUndefined(undefined); // Fails

Messages

assert.isUndefined.message = "Expected ${actual} to be undefined";
refute.isUndefined.message = "Expected not to be undefined";

isNull()

assert.isNull(object[, message])

Fails if object is not null.

assert.isNull(null); // Passes
assert.isNull({}); // Fails
refute.isNull({}); // Passes
refute.isNull(null); // Fails

Messages

assert.isNull.message = "Expected ${actual} to be null";
refute.isNull.message = "Expected not to be null";

match()

assert.match(actual, matcher[, message])

Fails if matcher is not a partial match for actual. Accepts a wide range of input combinations. Note that assert.match is not symmetric - in some cases assert.match(a, b) may pass while assert.match(b, a) fails.

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.

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

The last example is not symmetric. When the matcher is a string, the actual value is coerced to a string - in this case using toString. Changing the order of the arguments would cause the matcher to be an object, in which case different rules apply (see below).

Boolean matcher

Performs a strict (i.e. ===) match with the object. So, only true matches true, and only false matches false.

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); // Passes
assert.match("Give me something", /[0-9]/); // Fails
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); // Passes
assert.match("Give me something", 425); // Fails
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.

// Passes
assert.match("123", function (exp) {
  return exp == "123";
});

// Fails
assert.match("Give me something", function () {
  return "ok";
});

// Passes
assert.match(
  {
    toString: function () {
      return "42";
    },
  },
  function () {
    return true;
  },
);

// Fails
assert.match(234, function () {});

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. Note that the object matcher does not care if the number of properties in the two objects are the same - only if all properties in the matcher recursively “matches” ones in the actual object.

// Passes
assert.match("123", {
  test: function (arg) {
    return arg == 123;
  },
});

// Fails
assert.match({}, { prop: 42 });

// Passes
assert.match(
  {
    name: "Chris",
    profession: "Programmer",
  },
  {
    name: "Chris",
  },
);

// Fails
assert.match(234, {
  name: "Chris",
});

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",
});

isObject()

assert.isObject(object[, message])

Fails if object is not an object or if it is null.

assert.isObject({}); // Passes
assert.isObject(42); // Fails
assert.isObject([1, 2, 3]); // Passes
assert.isObject(function () {}); // Fails

Messages

assert.isObject.message =
  "${actual} (${actualType}) expected to be object and not null";
refute.isObject.message = "${actual} expected to be null or not an object";

isFunction()

assert.isFunction(actual[, message])

Fails if actual is not a function.

assert.isFunction({}); // Fails
assert.isFunction(42); // Fails
assert.isFunction(function () {}); // Passes

Messages

assert.isFunction.message = "${actual} (${actualType}) expected to be function";
refute.isFunction.message = "${actual} expected not to be function";

isTrue()

assert.isTrue(actual[, message])

Fails if actual is not true.

assert.isTrue("2" == 2); // Passes
assert.isTrue("2" === 2); // Fails

Messages

assert.isTrue.message = "Expected ${actual} to be true";
refute.isTrue.message = "Expected ${actual} to not be true";

isFalse()

assert.isFalse(actual[, message])

Fails if actual is not false.

assert.isFalse("2" === 2); // Passes
assert.isFalse("2" == 2); // Fails

Messages

assert.isFalse.message = "Expected ${actual} to be false";
refute.isFalse.message = "Expected ${actual} to not be false";

isString()

assert.isString(actual[, message])

Fails if the type of actual is not "string".

assert.isString("2"); // Passes
assert.isString(2); // Fails

Messages

assert.isString.message = "Expected ${actual} (${actualType}) to be string";
refute.isString.message = "Expected ${actual} not to be string";

isBoolean()

assert.isBoolean(actual[, message])

Fails if the type of actual is not "boolean".

assert.isBoolean(true); // Passes
assert.isBoolean(2 < 2); // Passes
assert.isBoolean("true"); // Fails

Messages

assert.isBoolean.message = "Expected ${actual} (${actualType}) to be boolean";
refute.isBoolean.message = "Expected ${actual} not to be boolean";

isNumber()

assert.isNumber(actual[, message])

Fails if the type of actual is not "number" or is NaN.

assert.isNumber(12); // Passes
assert.isNumber("12"); // Fails
assert.isNumber(NaN); // Fails

Messages

assert.isNumber.message =
  "Expected ${actual} (${actualType}) to be a non-NaN number";
refute.isNumber.message = "Expected ${actual} to be NaN or a non-number value";

isNaN()

assert.isNaN(actual[, message])

Fails if actual is not NaN. Does not perform coercion in contrast to the standard javascript function isNaN.

assert.isNaN(NaN); // Passes
assert.isNaN("abc" / "def"); // Passes
assert.isNaN(12); // Fails
assert.isNaN({}); // Fails, would pass for standard javascript function isNaN

Messages

assert.isNaN.message = "Expected ${actual} to be NaN";
refute.isNaN.message = "Expected not to be NaN";

isNegativeInfinity()

assert.isNegativeInfinity(actual[, message])

Fails if actual is not -Infinity.

assert.isNegativeInfinity(-Infinity); // Passes
assert.isNegativeInfinity(42); // Fails

isArray()

assert.isArray(actual[, message])

Fails if the object type of actual is not Array.

assert.isArray([1, 2, 3]); // Passes
assert.isArray({}); // Fails

Messages

assert.isArray.message = "Expected ${actual} to be array";
refute.isArray.message = "Expected ${actual} not to be array";

isArrayBuffer()

assert.isArrayBuffer(actual[, message])

Fails if the object type of actual is not ArrayBuffer.

assert.isArrayBuffer(new ArrayBuffer(8)); // Passes
assert.isArrayBuffer({}); // Fails

Messages

assert.isArrayBuffer.message = "Expected ${actual} to be an ArrayBuffer";
refute.isArrayBuffer.message = "Expected ${actual} not to be an ArrayBuffer";

isArrayLike()

assert.isArrayLike(actual[, message])

Fails if none of the following conditions are fulfilled:

  • the object type of actual is Array
  • actual is an arguments object
  • actual is an object providing a property length of type "number" and a property splice of type "function"
assert.isArrayLike([1, 2, 3]); // Passes
assert.isArrayLike(arguments); // Passes
assert.isArrayLike({ length: 0, splice: function () {} }); // Passes
assert.isArrayLike({}); // Fails

Messages

assert.isArrayLike.message = "Expected ${actual} to be array like";
refute.isArrayLike.message = "Expected ${actual} not to be array like";

isDataView()

assert.isDataView(actual[, message])

Fails if the object type of actual is not DataView.

assert.isDataView(new DataView(new ArrayBuffer(16)); // Passes
assert.isDataView({});                               // Fails

Messages

assert.isDataView.message = "Expected ${actual} to be a DataView";
refute.isDataView.message = "Expected ${actual} not to be a DataView";

isDate()

assert.isDate(actual[, message])

Fails if actual is not an instance of Date.

assert.isDate(new Date()); // Passes
assert.isDate(12345678); // Fails
assert.isDate("apple pie"); // Fails

Messages

assert.isDate.message = "Expected ${actual} to be a Date";
refute.isDate.message = "Expected ${actual} not to be a Date";

isError()

assert.isError(actual[, message])

Fails if actual is not an instance of Error. Passes for all built in error types and errors derived from a built in error type.

assert.isError(new Error("this is an error"); // Passes
assert.isError("this is not an error");       // Fails

Messages

assert.isError.message = "Expected ${actual} to be an Error";
refute.isError.message = "Expected ${actual} not to be an Error";

isEvalError()

assert.isEvalError(actual[, message])

Fails if actual is not an instance of EvalError.

assert.isEvalError(new EvalError("this is an eval error")); // Passes
assert.isEvalError(new Error("this is not an eval error")); // Fails

Messages

assert.isEvalError.message = "Expected ${actual} to be an EvalError";
refute.isEvalError.message = "Expected ${actual} not to be an EvalError";

isFloat32Array()

assert.isFloat32Array(actual[, message])

Fails if actual is not an instance of Float32Array.

assert.isFloat32Array(new Float32Array(2)); // Passes
assert.isFloat32Array(new Float64Array(2)); // Fails

Messages

assert.isFloat32Array.message = "Expected ${actual} to be a Float32Array";
refute.isFloat32Array.message = "Expected ${actual} not to be n Float32Array";

isFloat64Array()

assert.isFloat64Array(actual[, message])

Fails if actual is not an instance of Float64Array.

assert.isFloat64Array(new Float64Array(2)); // Passes
assert.isFloat64Array(new Float32Array(2)); // Fails

Messages

assert.isFloat64Array.message = "Expected ${actual} to be a Float64Array";
refute.isFloat64Array.message = "Expected ${actual} not to be a Float64Array";

isInfinity()

assert.isInfinity(actual[, message])

Fails if actual is not Infinity.

assert.isInfinity(Infinity); // Passes
assert.isInfinity(42); // Fails

Messages

assert.isInfinity.message = "Expected ${actual} to be Infinity";
refute.isInfinity.message = "Expected ${actual} not to be Infinity";

isInt8Array()

assert.isInt8Array(actual[, message])

Fails if actual is not an instance of Int8Array.

assert.isInt8Array(new Int8Array(2)); // Passes
assert.isInt8Array(new Int16Array(2)); // Fails

Messages

assert.isInt8Array.message = "Expected ${actual} to be an Int8Array";
refute.isInt8Array.message = "Expected ${actual} not to be an Int8Array";

isInt16Array()

assert.isInt16Array(actual[, message])

Fails if actual is not an instance of Int16Array.

assert.isInt16Array(new Int16Array(2)); // Passes
assert.isInt16Array(new Int32Array(2)); // Fails

Messages

assert.isInt16Array.message = "Expected ${actual} to be an Int16Array";
refute.isInt16Array.message = "Expected ${actual} not to be an Int16Array";

isInt32Array()

assert.isInt32Array(actual[, message])

Fails if actual is not an instance of Int32Array.

assert.isInt32Array(new Int32Array(2)); // Passes
assert.isInt32Array(new Int16Array(2)); // Fails

Messages

assert.isInt32Array.message = "Expected ${actual} to be an Int32Array";
refute.isInt32Array.message = "Expected ${actual} not to be an Int32Array";

isIntlCollator()

assert.isIntlCollator(actual[, message])

Fails if actual is not an instance of Intl.Collator.

assert.isIntlCollator(new Intl.Collator()); // Passes
assert.isIntlCollator({}); // Fails

Messages

assert.isIntlCollator.message = "Expected ${actual} to be an Intl.Collator";
refute.isIntlCollator.message = "Expected ${actual} not to be an Intl.Collator";

isIntlDateTimeFormat()

assert.isIntlDateTimeFormat(actual[, message])

Fails if actual is not an instance of Intl.DateTimeFormat.

assert.isIntlDateTimeFormat(new Intl.DateTimeFormat()); // Passes
assert.isIntlDateTimeFormat({}); // Fails

Messages

assert.isIntlDateTimeFormat.message =
  "Expected ${actual} to be an Intl.DateTimeFormat";
refute.isIntlDateTimeFormat.message =
  "Expected ${actual} not to be an Intl.DateTimeFormat";

isIntlNumberFormat()

assert.isIntlNumberFormat(actual[, message])

Fails if actual is not an instance of Intl.NumberFormat.

assert.isIntlNumberFormat(new Intl.NumberFormat()); // Passes
assert.isIntlNumberFormat({}); // Fails

Messages

assert.isIntlNumberFormat.message =
  "Expected ${actual} to be an Intl.NumberFormat";
refute.isIntlNumberFormat.message =
  "Expected ${actual} not to be an Intl.NumberFormat";

isMap()

assert.Map(actual[, message])

Fails if actual is not an instance of Map.

assert.isMap(new Map()); // Passes
assert.isMap({}); // Fails

Messages

assert.isMap.message = "Expected ${actual} to be a Map";
refute.isMap.message = "Expected ${actual} not to be a Map";

isPromise()

assert.Promise(actual[, message])

Fails if actual is not an instance of Promise.

assert.isPromise(new Promise()); // Passes
assert.isPromise({}); // Fails

Messages

assert.isPromise.message = "Expected ${actual} to be a Promise";
refute.isPromise.message = "Expected ${actual} not to be a Promise";

isRangeError()

assert.isRangeError(actual[, message])

Fails if actual is not an instance of RangeError.

assert.isRangeError(new RangeError("this is a range error")); // Passes
assert.isRangeError(new Error("this is not a range error")); // Fails

Messages

assert.isRangeError.message = "Expected ${actual} to be an RangeError";
refute.isRangeError.message = "Expected ${actual} not to be an RangeError";

isReferenceError()

assert.isReferenceError(actual[, message])

Fails if actual is not an instance of ReferenceError.

assert.isReferenceError(new ReferenceError("this is a range error")); // Passes
assert.isReferenceError(new Error("this is not a range error")); // Fails

Messages

assert.isReferenceError.message = "Expected ${actual} to be a ReferenceError";
refute.isReferenceError.message =
  "Expected ${actual} not to be a ReferenceError";

isRegExp()

assert.isRegExp(actual[, message])

Fails if actual is not an instance of RegExp.

assert.isRegExp(new RegExp("apple pie")); // Passes
assert.isRegExp(/apple pie/); // Passes
assert.isRegExp("apple pie"); // Fails

Messages

assert.isRegExp.message = "Expected ${actual} to be an RegExp";
refute.isRegExp.message = "Expected ${actual} not to be an RegExp";

isSet()

assert.isSet(actual[, message])

Fails if actual is not an instance of Set.

assert.isSet(new Set()); // Passes
assert.isSet([]); // Fails

Messages

assert.isSet.message = "Expected ${actual} to be a Set";
refute.isSet.message = "Expected ${actual} not to be a Set";

isSymbol()

assert.isSymbol(actual[, message])

Fails if actual is not a value of type Symbol.

assert.isSymbol(Symbol("apple pie")); // Passes
assert.isSymbol("apple pie"); // Fails

Messages

assert.isSymbol.message = "Expected ${actual} to be a Symbol";
refute.isSymbol.message = "Expected ${actual} not to be a Symbol";

isSyntaxError()

assert.isSyntaxError(actual[, message])

Fails if actual is not an instance of SyntaxError.

assert.isSyntaxError(new SyntaxError("this is a syntax error")); // Passes
assert.isSyntaxError(new Error("this is not a syntax error")); // Fails

Messages

assert.isSyntaxError.message = "Expected ${actual} to be a SyntaxError";
refute.isSyntaxError.message = "Expected ${actual} not to be a SyntaxError";

isTypeError()

assert.isTypeError(actual[, message])

Fails if actual is not an instance of TypeError.

assert.isTypeError(new TypeError("this is a type error")); // Passes
assert.isTypeError(new Error("this is not a type error")); // Fails

Messages

assert.isTypeError.message = "Expected ${actual} to be a TypeError";
refute.isTypeError.message = "Expected ${actual} not to be a TypeError";

isURIError()

assert.isURIError(actual[, message])

Fails if actual is not an instance of URIError.

assert.isURIError(new URIError("this is a uri error")); // Passes
assert.isURIError(new Error("this is not a uri error")); // Fails

Messages

assert.isURIError.message = "Expected ${actual} to be a URIError";
refute.isURIError.message = "Expected ${actual} not to be a URIError";

isUint16Array()

assert.isUint16Array(actual[, message])

Fails if actual is not an instance of Uint16Array.

assert.isUint16Array(new Uint16Array()); // Passes
assert.isUint16Array(new Uint32Array()); // Fails
assert.isUint16Array(new Uint8Array()); // Fails

Messages

assert.isUint16Array.message = "Expected ${actual} to be a Uint16Array";
refute.isUint16Array.message = "Expected ${actual} not to be a Uint16Array";

isUint32Array()

assert.isUint32Array(actual[, message])

Fails if actual is not an instance of Uint32Array.

assert.isUint32Array(new Uint16Array()); // Fails
assert.isUint32Array(new Uint32Array()); // Passes
assert.isUint32Array(new Uint8Array()); // Fails

Messages

assert.isUint32Array.message = "Expected ${actual} to be a Uint32Array";
refute.isUint32Array.message = "Expected ${actual} not to be a Uint32Array";

isUint8Array()

assert.isUint8Array(actual[, message])

Fails if actual is not an instance of Uint8Array.

assert.isUint8Array(new Uint16Array()); // Fails
assert.isUint8Array(new Uint32Array()); // Fails
assert.isUint8Array(new Uint8Array()); // Passes

Messages

assert.isUint8Array.message = "Expected ${actual} to be a Uint8Array";
refute.isUint8Array.message = "Expected ${actual} not to be a Uint8Array";

isUint8ClampedArray()

assert.isUint8ClampedArray(actual[, message])

Fails if actual is not an instance of Uint8ClampedArray.

assert.isUint8ClampedArray(new Uint8ClampedArray()); // Passes
assert.isUint8ClampedArray(new Uint8Array()); // Fails

Messages

assert.isUint8ClampedArray.message =
  "Expected ${actual} to be a Uint8ClampedArray";
refute.isUint8ClampedArray.message =
  "Expected ${actual} not to be a Uint8ClampedArray";

isWeakMap()

assert.isWeakMap(actual[, message])

Fails if actual is not an instance of WeakMap.

assert.isWeakMap(new WeakMap()); // Passes
assert.isWeakMap(new Map()); // Fails

Messages

assert.isWeakMap.message = "Expected ${actual} to be a WeakMap";
refute.isWeakMap.message = "Expected ${actual} not to be a WeakMap";

isWeakSet()

assert.isWeakSet(actual[, message])

Fails if actual is not an instance of WeakSet.

assert.isWeakSet(new WeakSet()); // Passes
assert.isWeakSet(new Set()); // Fails

Messages

assert.isWeakSet.message = "Expected ${actual} to be a WeakSet";
refute.isWeakSet.message = "Expected ${actual} not to be a WeakSet";

keys()

assert.keys(object, keyArray[, message])

Fails if object’s own properties are not exactly the same as a given list.

assert.keys({ test1: "t1", test2: "t2" }, ["test1"]); // Fails - 'test2' is unexpected
assert.keys({ test1: "t1", test2: "t2" }, ["test1", "test2", "test3"]); // Fails - 'test3' is not present
assert.keys({ test1: "t1", test2: "t2" }, ["test1", "test2"]); // Passes

Messages

assert.keys.message = "Expected ${actualObject} to have exact keys ${keys}";
refute.keys.message = "Expected not to have exact keys ${keys}";

exception()

assert.exception(callback[, matcher, message])

Fails if callback does not throw an exception. If the optional matcher is provided, the assertion fails if the callback either does not throw an exception, or if the exception does not meet the criterias of the given matcher.

The matcher can be of type object or function. If the matcher is of type object, the captured error object and the matcher are passed to match().

If the matcher is of type function, the captured error object is passed as argument to the matcher function, which has to return true for a matching error object, otherwise false.

// Passes
assert.exception(function () {
  throw new Error("Ooops!");
});

// Fails
assert.exception(function () {});

// Passes
assert.exception(
  function () {
    throw new TypeError("Ooops!");
  },
  { name: "TypeError" },
);

// Fails, wrong exception type
assert.exception(
  function () {
    throw new Error("Aww");
  },
  { name: "TypeError" },
);

// Fails, wrong exception message
assert.exception(
  function () {
    throw new Error("Aww");
  },
  { message: "Ooops!" },
);

// Fails, wrong exception type
assert.exception(
  function () {
    throw new Error("Aww");
  },
  function (err) {
    if (err.name !== "TypeError") {
      return false;
    }
    return true;
  },
  "Type of exception is wrong!",
); // with message to print, if test fails

near()

assert.near(actual, expected, delta[, message])

Fails if the difference between actual and expected is greater than delta.

assert.near(10.3, 10, 0.5); // Passes
assert.near(10.5, 10, 0.5); // Passes
assert.near(10.6, 10, 0.5); // Fails

Messages

assert.near.message =
  "Expected ${actual} to be equal to ${expected} +/- ${delta}";
refute.near.message =
  "Expected ${actual} not to be equal to ${expected} +/- ${delta}";

hasArity()

assert.hasArity(actual, arity[, message])

Fails when actual does not have the desired arity.

assert.hasArity(function (one) {
  return one;
}, 1); // Passes
assert.hasArity(function (one, two) {
  return one + two;
}, 2); // Passes
assert.hasArity(function (one, two) {
  return one + two;
}, 1); // Fails

Messages

assert.hasArity.message =
  "Expected ${name} to have arity of ${1} but was ${arity}";
refute.hasArity.message = "Expected ${name} to not have arity of ${1}";

hasPrototype()

assert.hasPrototype(actual, prototype[, message])

Fails if prototype does not exist in the prototype chain of actual.

assert.hasPrototype(function () {}, Function.prototype); // Passes
assert.hasPrototype(function () {}, Object.prototype); // Passes
assert.hasPrototype({}, Function.prototype); // Fails

Messages

assert.hasPrototype.message =
  "Expected ${actual} to have ${expected} on its prototype chain";
refute.hasPrototype.message =
  "Expected ${actual} not to have ${expected} on its prototype chain";

contains()

assert.contains(haystack, needle[, message])

Fails if the array like object haystack does not contain the needle argument.

assert.contains([1, 2, 3], 2); // Passes
assert.contains([1, 2, 3], 4); // Fails
assert.contains([1, 2, 3], "2"); // Fails

Messages

assert.contains.message = "Expected [${actual}] to contain ${expected}";
refute.contains.message = "Expected [${actual}] not to contain ${expected}";

tagName()

assert.tagName(element, tagName[, message])

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. Works with any object.

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

className()

assert.className(element, classNames[, message])

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.

var el = document.createElement("p");
el.className = "feed item blog-post";

assert.className(el, "item"); // Passes
assert.className(el, "news"); // Fails
assert.className(el, "blog-post feed"); // Passes
assert.className(el, "feed items"); // Fails, "items" is not a match
assert.className(el, ["item", "feed"]); // Passes

resolves()

assert.resolves(promise[, value])

NOTE: This assertion returns a Promise!

The assertion resolves, if the value resolved from promise equals the given value.

Furthermore, the assertion rejects if,

  • the resolved value from the input promise is not equal to the given value
  • the given promise rejects instead of resolving
  • the given input is not a Promise

NOTE: In order to tell the test runner to wait until the assertion has completed, you either need to return the Promise from the assertion:

test("some asynchronous code", function() {
    return assert.resolves(myAsyncFunc(), {...});
});

or use async/await:

test("some asynchronous code", async function() {
    await assert.resolves(myAsyncFunc(), {...});
});

rejects()

assert.rejects(promise[, value])

NOTE: This assertion returns a Promise!

The assertion resolves, if the value rejected from promise equals the given value.

Furthermore, the assertion rejects if,

  • the rejected value from the input promise is not equal to the given value
  • the given promise resolves instead of rejecting
  • the given input is not a Promise

NOTE: In order to tell the test runner to wait until the assertion has completed, you either need to return the Promise from the assertion:

test("some asynchronous code", function() {
    return assert.rejects(myAsyncFunc(), {...});
});

or use async/await:

test("some asynchronous code", async function() {
    await assert.rejects(myAsyncFunc(), {...});
});

json()

assert.json(actual, json[, message])

Fails if actual is not valid JSON or the parsed JSON is not equal to json. Uses the same comparison algorithm as equals().

var serialized = JSON.stringify({ is: 42 });

assert.json(serialized, { is: 42 }); // Passes
assert.json(serialized, { or: 42 }); // Fails
assert.json(serialized, { is: 7 }); // Fails
assert.json("no-json", {}); // Fails

matchJson()

assert.matchJson(actual, json[, message])

Fails if actual is not valid JSON or the parsed JSON does not match json. Uses the same matcher algorithm as match().

var serialized = JSON.stringify({ is: 42, and: 3 });

assert.matchJson(serialized, { is: 42 }); // Passes
assert.matchJson(serialized, { and: 3 }); // Passes
assert.matchJson(serialized, { or: 42 }); // Fails
assert.matchJson(serialized, { is: 7 }); // Fails
assert.matchJson("no-json", {}); // Fails

Custom assertions

Custom, domain-specific assertions help improve clarity and reveal intent in tests. They also facilitate much better feedback when they fail. You can add custom assertions that behave exactly like the built-in ones (i.e. with counting, message formatting, expectations and more) by using the referee.add method.

Load custom assertions

Custom assertions can be loaded as modules using e.g. mocha to be used in tests.

Custom assertion

./test/assertions/is-prime.js

const referee = require("referee");

// adapted from https://stackoverflow.com/a/40200710
function isPrime(number) {
  for (var i = 2; i < number; i++) {
    if (number % i === 0) {
      return false;
    }
  }
  return number !== 1 && number !== 0;
}

referee.add("isPrime", {
  assert: function assert(actual) {
    if (typeof actual !== "number" || actual < 0) {
      throw new TypeError("'actual' argument should be a non-negative Number");
    }

    this.actual = actual;

    return isPrime(actual);
  },
  assertMessage: "Expected ${actual} to be a prime number",
  refuteMessage: "Expected ${actual} to not be a prime number",
  expectation: "toHaveArity",
});

Test

./test/some.test.js

const { assert, refute } = require("referee");

describe("some", function () {
  it("should have isPrime installed", function () {
    assert.isPrime(5);
    refute.isPrime(6);
  });
});

Execute

mocha -r ./test/assetions/*

This repository hosts the full runnable example from above, as well as demos using other popular test runners.

Overriding assertion messages

The default assertion messages can be overridden. The messages to overwrite are listed with each assertion. You can use the same keys for string interpolation (e.g. ${actual}, ${expected}). equals:

var assert = require("referee").assert;
assert.equals.message = "I wanted ${actual} == ${expected}!";

try {
  assert.equals(3, 4);
} catch (e) {
  console.log(e.message);
}

// Prints:
// "I wanted 3 == 4!"

Events

referee is an event-emitter. Listen to events with on:

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

pass event

Signature:

"pass", function () {};

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

failure event

Signature:

"failure", function (error) {};

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

Expectations

All of referee's assertions and refutations are also exposed as "expectations". Expectations is just a slightly different front-end to the same functionality, often preferred by the BDD inclined.

Expectations mirror assertions under different names. Refutations can be expressed using expect(obj).not and then calling either of the expectations on the resulting object.

var expect = require("referee").expect;

expect({ id: 42 }).toBeObject(); // Passes
expect("Somewhere in here").toMatch("in"); // Passes
expect(42).not.toEqual(43); // Passes

expect.toBe()

expect(actual).toBe(expected);

See same()

expect.toEqual()

expect(actual).toEqual(expected);

See equals()

expect.toBeGreaterThan()

expect(actual).toBeGreaterThan(expected);

See greater()

expect.toBeLessThan()

expect(actual).toBeLessThan(expected);

See less()

expect.toBeDefined()

expect(actual).toBeDefined(expected);

See defined()

expect.toBeNull()

expect(actual).toBeNull(expected);

See isNull()

expect.toMatch()

expect(actual).toMatch(expected);

See match()

expect.toBeObject()

expect(actual).toBeObject(expected);

See isObject()

expect.toBeFunction()

expect(actual).toBeFunction(expected);

See isFunction()

expect.toBeTrue()

expect(actual).toBeTrue();

See isTrue()

expect.toBeFalse()

expect(actual).toBeFalse();

See isFalse()

expect.toBeString()

expect(actual).toBeString();

See isString()

expect.toBeBoolean()

expect(actual).toBeBoolean();

See isBoolean()

expect.toBeNumber()

expect(actual).toBeNumber();

See isNumber()

expect.toBeNaN()

expect(actual).toBeNaN();

See isNaN()

expect.toBeArray()

expect(actual).toBeArray();

See isArray()

expect.toBeArrayLike()

expect(actual).toBeArrayLike();

See isArrayLike()

expect.toHaveKeys()

expect(object).toHaveKeys(keyArray);

See keys()

expect.toThrow()

expect(actual).toThrow(expected);

See exception()

expect.toBeNear()

expect(actual).toBeNear(expected, delta);

See near()

expect.toHavePrototype()

expect(actual).toHavePrototype(prototype);

See hasPrototype()

expect.toContain()

expect(haystack).toContain(needle);

See contains()

expect.toHaveTagName()

expect(actual).toHaveTagName(expected);

See tagName()

expect.toHaveClassName()

expect(actual).toHaveClassName(expected);

See className()

expect.toEqualJson()

expect(actual).toEqualJson(expected);

See json()

expect.toMatchJson()

expect(actual).toMatchJson(expected);

See matchJson()

expect.toHaveBeenCalled()

expect(spy).toHaveBeenCalled();

See called()

expect.toHaveBeenCalledOnce()

expect(spy).toHaveBeenCalledOnce(expected);

See calledOnce()

expect.toHaveBeenCalledTwice()

expect(spy).toHaveBeenCalledTwice(expected)

See calledTwice()

expect.toHaveBeenCalledThrice()

expect(spy).toHaveBeenCalledThrice(expected);

See calledThrice()

expect.toHaveBeenCalledWith()

expect(spy).toHaveBeenCalledWith(arg1, arg2, ...)

See calledWith()

expect.toHaveBeenCalledOnceWith()

expect(spy).toHaveBeenCalledOnceWith(arg1, arg2, ...)

See calledOnceWith()

Methods

referee.fail()

referee.fail(message);

When an assertion fails, it calls referee.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 referee.fail() to make it do the right thing.

Example: To use referee with JsTestDriver, you can simply configure it as follows:

referee.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.

referee.add()

referee.add(name, options);

Add 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
  • 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 (may be important for asynchronous tests)
  • The assertion will fail if too few arguments are passed

Here’s an example of adding a “foo” assertion, that only passes when its only argument is the string “foo”:

var assert = referee.assert;
var refute = referee.refute;
var expect = referee.expect;

referee.add("isFoo", {
  assert: function (actual) {
    return actual == "foo";
  },
  assertMessage: "Expected ${0} to be foo!",
  refuteMessage: "Expected not to be foo!",
  expectation: "toBeFoo",
});

// Now you can do:
// Passes
assert.isFoo("foo");

// Fails: "[assert.isFoo] Expected { id: 42 } to be foo!"
assert.isFoo({ id: 42 });

// Fails: "[refute.isFoo] Expected not to be foo!"
refute.isFoo("foo");

// Passes
expect("foo").toBeFoo();

// To support custom messages, do this:
referee.add("isFoo", {
  assert: function (actual) {
    return actual == "foo";
  },
  assertMessage: "${1}Expected ${0} to be foo!",
  refuteMessage: "${1}Expected not to be foo!",
  expectation: "toBeFoo",
  values: function (thing, message) {
    return [thing, message ? message + " " : ""];
  },
});

// Fails: "[assert.isFoo] Ouch: Expected { id: 42 } to be foo!"
assert.isFoo({ id: 42 }, "Ouch");

Error message value interpolation

Arguments are available in assertion failure messages using the "${n}" switches, where n is a number. You can also use named variables by setting properties on this in the assertion/refutation function:

referee.add("isString", {
  assert: function (actual) {
    this.actualType = typeof actual;
    return this.actualType == "string";
  },
  assertMessage: "Expected ${0} (${actualType}) to be string",
  refuteMessage: "Expected not to be string",
  expectation: "toBeString",
});
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 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, referee 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). The message is exposed on the generated assertion as the property assert.[name].message.
refuteMessage
Like assertMessage, but for refutations. Exposed as refute.[name].message.
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.
expectation
The name of the assertion as an expectation, e.g. “toBeSomething”. Optional.

referee.verifier()

Creates a verifier function with the signature verify([expected]).

It exposes verify.count which is incremented every time referee emits a "pass" or a "failure" event. When called, the function verifies that assertions have been made, throwing an exception if verify.count is zero. When expected is passed, then this is compared with verify.count.

verify() always unsubscribes the event listeners, so that verify.count does not change. This means you have to create a new verifier for each test run.

it("should do something", function () {
  var verify = referee.verifier();

  var limit = 10;
  for (var i = 0; i < 10; i++) {
    console.log(i);
  }

  // this test will fail as no assertions have been made
  verify();
});

it("should do something", function () {
  var verify = referee.verifier();

  var limit = 10;
  for (var i = 0; i < 10; i++) {
    assert.isTrue(true);
  }

  // this will pass because exactly 10 (`limit`) assertions have been made
  verify(limit);

  console.log(verify.count);
  // 10
});

Properties

referee.throwOnFailure

Boolean.

When using the default referee.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

class AssertionError()

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