Core Plugin Concepts

Plugins are for more than just writing vendor integrations. As a tester, one can write a plugin to validate input data, assert schema validation on an object, or ensure proper behavior on a DOM element. The API is flexible enough that any synchronous tasks can easily be encapsulated within a single assertion and reused throughout your tests.

This tutorial will show you how to access Chai’s plugin API, use flags to transfer data through the language chain, and write your first assertion (and thorough error messages). Once you have finished here, Building a Helper will show you how to compose properties and methods for use on the Chai language chain.

Accessing Utilities

Chai comes with a number of utilities to assist in the construction of assertions, but it does not provide these directly on the chai export. These can be accessed by using the use method of the chai export, which accepts a single function as an argument.

chai.use(function (_chai, utils) {
  // ...
});

The function which will be used gets passed two parameters to its scope. The first is the chai export, the second is an object containing a number of utility methods (we’ll get to those in a minute).

The chai export is included so that you can build helpers that can be used in multiple test files, or package your helpers as a plugin to share with the community. A more appropriate pattern for creating helpers is as follows…

For our helper file: test/helpers/model.js

module.exports = function (chai, utils) {
  let Assertion = chai.Assertion;

  // your helpers here
};

And, for our actual test: test/person.js

let chai = require("chai"),
  chaiModel = require("./helpers/model"),
  expect = chai.expect;

chai.use(chaiModel);

For the rest of this document, we will assume this structure…

The Assertion variable is now a constructor for an assertion chain; new Assertion(obj) is now equivalent to expect(obj).

Using Flags

The upper-most core concept of how assertions work internally is the concept of flags. Each assertion has a set of mostly arbitrary flags—key:value pairs—associated with it. Chai uses a small number of these internally, but the store is also available for developers to expand on.

Flag Usage

The flag utility is exposed as utils.flag from within our use function. It can function as either a getter or a setter, depending on the number of arguments passed to it.

let myAssert = new Assertion(obj);
utils.flag(myAssert, "owner", "me"); // sets key `owner` to `me`
let owner = utils.flag(myAssert, "owner"); // gets key `owner`, returns value

Object Flag

The most important of Chai’s reserved flags is the object flag. This is the subject of an assertion.

let myAssert = new Assertion("Arthur Dent");
let obj = utils.flag(myAssert, "object"); // obj === 'Arthur Dent';

This flag is so often used that a shortcut was provided as the _obj property of a constructed assertion.

let obj = myAssert._obj; // obj === 'Arthur Dent'

The following flags are used by Chai’s core assertions. Side effects may occur should you interfere with these.

Composing an Assertion

Before we begin adding methods and properties to the language chain, we should first examine how to invoke an assertion and the expected behavior should it fail.

For this, each constructed Assertion has a method called simply assert. It accepts many parameters, and its behavior can change depending on whether an assertion was negated or not.

Basic Assertion

To begin, we will construct Arthur again, then we can assert that he is who he says he is.

let arthur = new Assertion("Arthur Dent");

arthur.assert(
  arthur._obj === "Arthur Dent",
  "expected #{this} to be 'Arthur Dent'",
  "expected #{this} to not be 'Arthur Dent'",
);

Chai will check the first argument; if it is true, then the assertion passed. If it is false, the assertion failed, and the first error message will be thrown as part of a chai.AssertionError. Conversely, if the language chain was negated, it will consider false a pass and true a failure. The second error message will be included in the thrown error instead.

In all, the assert method accepts six arguments:

  1. a boolean (result of a truth test)
  2. a string error message to be used if the first argument is false
  3. a string error message to be used if the assertion is negated and the first argument is true
  4. (optional) the expected value
  5. (optional) the actual value, which will default to _obj
  6. (optional) a boolean indicating whether to display a diff in addition to the message if the first argument is false

Composing Error Messages

As you can see from the above example, Chai can accept template tags to dynamically compose the error message. When used, these template tags will be replaced with a stringified representation of the object in question. There are three template tags available.