Expectacle is a simple expectation library with a sane syntax.
Expectacle can be used in the browser, on NodeJS and on any CommonJS environment that supports Modules/1.1.
Download expectacle.js
and include it with a script tag:
<script type="text/javascript" src="/path/to/expectacle.js"></script>
<script type="text/javascript">
// window.expect();
</script>
Expectacle is available on NPM:
$ npm install expectacle
You can then require it in your tests:
var expect = require('expectacle');
Download expectacle.js
and use require
:
var expect = require('./path/to/expectacle').expect;
Expectacle has one main function called expect
, that is used to create a new expectation. It takes in one argument, the value of the expectation, which could be of any JavaScript type.
expect(1);
An expectation can then be tested using matchers. A matcher is a function that performs checks on the value of the expectation using zero or one arguments. It then returns either true
or false
depending on whether the value passes the check. If a matcher returns false
, the expectation fails and an error is thrown.
expect(1).toBe(1); // passes, no error.
expect(2).toBe(1); // fails, throws an ExpectationError.
All expectations have a corresponding reversed expectation that can be accessed using the not
member. This expectation has the same matchers as a regular expectations, but perform reversed tests with the matchers: the matcher returns false
for success and true
for failures.
expect(1).not.toBe(2); // passes, no error.
expect(1).not.toBe(1); // fails, throws an ExpectationError.
Expect also takes an optional description as its second parameter, which can be used to add information to the expectation error:
expect(1, 'number').toBe(2); // fails: "Expected number 1 to be 2"
Expectacle comes with the following matchers by default:
The identity matcher checks whether the actual value is identical to the expected value. It uses the ===
operator internally, and therefore disregards any type casting rules.
The equality matcher checks whether the actual value is equal to the expected value. It uses the ==
operator internally, and therefore coerces types.
Checks whether the actual value is similar to the expected value. It uses a deep-comparison of keys and properties for objects.
Checks whether a function throws an error.
This matcher can be used without passing the error
argument, in which case the matcher only checks if the function throws.
Optionally, one could pass an error
argument that could either be a string, a regular expression or a constructor function:
- If the
error
argument is a string, theerror
argument is compared against themessage
of the thrown error. - If the
error
argument is a regular expression, themessage
of the thrown error is tested against theerror
argument. - If the
error
argument is a constructor function, thename
property of the thrown error is compared against thename
value of the constructor function'sprototype
.
Checks whether the actual value's string representation matches the passed regularExpression
argument.
Checks whether the actual value is of the passed length
.
This matcher uses the length
property if it is available (e.g., in strings, arrays, arguments and any object that has a length
property). If the actual value is an object without a length property, this matcher check the number of keys (i.e., member/property names).
Checks whether the actual value is empty.
This matcher uses the length
property if it is available (e.g., in strings, arrays, arguments and any object that has a length
property). If the actual value is an object without a length property, this matcher check the number of keys (i.e., member/property names).
Checks whether the actual value has a member (i.e., property or method) with a name corresponding to the passed argument.
Matches like toHaveMember
, but disregards any inherited members.
Checks whether the actual value has a property (i.e., non-function member) with a name corresponding to the passed argument.
Matches like toHaveProperty
, but disregards any inherited properties.
Checks whether the actual value has a method (i.e., function member) with a name corresponding to the passed argument.
Matches like toHaveMember
, but disregards any inherited methods.
Checks whether the expectation's value is an instance of the the passed constructor
argument. Will throw an ExpectationError
if the passed constructor
argument is not a function.
Checks whether the expectation's value has the type corresponding to the passed typeString
.
This function is implemented using Object.prototype.toString
to get the type's string representation. The following typeStrings are pre-populated: 'arguments'
, 'array'
, 'boolean'
, 'date'
, 'function'
,'null'
, 'number'
, 'object'
, 'regexp'
, 'string'
, 'undefined'
.
Checks whether the actual value is a boolean.
Checks whether the actual value is an array.
Checks whether the actual value is a function.
Checks whether the actual value is a number.
Checks whether the actual value is an object (but not an array).
Checks whether the actual value is a string.
Checks whether the actual value is null
.
Checks whether the actual value is undefined
.
Checks whether the actual value is NaN
.
Checks whether the actual value is true
.
Checks whether the actual value is false
.
Checks whether the actual value is truthy (i.e., coerces as a boolean true
value).
Checks whether the actual value is falsy (i.e., coerces as a boolean false
value).
Checks whether the actual object has a particular shape. See the "Shapes" section below.
Shapes are a way to verify the structure of the actual value in your tests, without verifying the actual value.
You can use the toHaveShape()
method, passing it a shape:
expect({
str: 'something',
num: 1,
bool: true
}).toHaveShape({
str: expect.shape.String(),
num: expect.shape.Number(),
bool: expect.shape.Boolean()
});
Shapes are not just limited to objects. You can check the shape of an array as well:
// Check that all array members are of a particular type:
expect([1, 2, 3]).toHaveShape(expect.shape.Array(expect.shape.Number()));
// Check that array members match particular shapes:
expect([1, 'string', {key: 'name'}]).toHaveShape(expect.shape.LiteralArray([
expect.shape.Number(),
expect.shape.String(),
{key: expect.shape.String()}
]));
All built-in shapes are available via expect.shape
.
Checks the actual value is of type "arguments"
.
When provided with a shape
, checks whether the actual value is an array, each element of which has the provided shape
.
If no shape
is provided, it only checks that the actual value is an array.
Checks the actual value is of type "boolean"
.
Checks the actual value is of type "date"
.
Checks the actual value is of type "function"
.
Checks that the actual value is equal (===
) to the given value
.
Checks that each item in the actual array value corresponds to the shape in the same index in the provided shapesArray
.
Checks the actual value is of type "null"
.
Checks the actual value is of type "number"
.
If an object shapeDescriptor
is provided, checks if the actual object value's properties are of the shape of the corresponding shapeDescriptor
.
If no shapeDescriptor
is provided, it simply checks if the actual value is an object.
Checks the actual value is of type "regexp"
.
Checks the actual value is of type "string"
.
Checks the actual value is of type "undefined"
.
You can add a custom matcher function using the expect.addMatcher
function:
expect.addMatcher(name, matcherFunction);
A matcherFunction
is a function that receives two arguments and returns a boolean. The first argument is the actual value (i.e., the value passed to the expect
function), and the second argument is the value passed to the matcher function when called (which can be empty; more on that in a bit).
expect.addMatcher('toStartWith', function(actual, prefix) {
if (prefix == expect.NULL_VALUE || !prefix) {
expect.fail('toStartWith requires a prefix argument.'); // throw an error
}
return actual.toString().indexOf(prefix) == 0;
});
expect('hello').toStartWith('he'); // passes.
expect('hello').not.toStartWith('he'); // fails; throws an error.
expect('hello').toStartWith(); // fails; throws an error because no prefix.
The matcherFunction
must return true
if the actual value passes the check and false
if it doesn't. Expectacle handles throwing the function automatically, so a simple boolean return value will suffice. The addMatcher
function also creates a reversed matcher automatically.
In cases where the user of the matcher does not provide an argument to the matcher, a special value expect.NULL_VALUE
is passed to the matcher instead. This is done in order to distinguish when the user passes values like undefined
and null
or when they just fail to provide an argument entirely.
The function expect.typeOf
can be used to get the type name of a value. This function is used internally by the default matchers.
The function expect.fail
can be used to raise an ExpectationError. It takes one argument, which is used as the error message.
Copyright 2022, Mark "Keeto" Obcena. Released under an MIT-Style License.