Developing Node.js applications with Certainty

Certainty is a JavaScript assertion framework designed to make your tests and their error messages more readable and discoverable, while being extensible to new types of objects.

Certainty is largely inspired by Truth, a Java-based testing framework created by Google. It is designed to work well with existing test frameworks such as Mocha, and with mocking libraries such as Sinon.

Certainty’s main goal is to produce “friendly”, helpful error messages. When I first started writing unit tests in Node.js, I was often frustrated by cryptic failure messages such as “expected false to be true”, which doesn’t tell you anything about what the failure is except that it involves a boolean.

Certainty uses a fluent syntax to build up complex propositions that read sort of like English sentences:

ensure(someValue).named(‘request’).hasField(‘index’).withValue(10);

If this assertion were to fail, it will produce an error message that will look something like this:

Expected request to have property named “index” with value 10, actual value was 20.

As you can see, you can give a name to the value being tested, and that name will be used in the error message. This is optional; if you decide not to give a name to the value, Certainty will try and describe the value in a way that is succinct and meaningful.

The ensure() function is the primary method of Certainty. (There is also expect() which merely prints the error but doesn’t abort the test.) The ensure function returns a Subject, which is a class that has various assertion methods on it. Depending on what kind of value is being tested, ensure() will return various different types of subjects: StringSubject for strings, ArraySubject for arrays, PromiseSubject for promises, and so on.

Each of the different subject types supports different methods. For example, here are just a few of the methods provided by StringSubject:

ensure(someString).includes(subString);
ensure(someString).startsWith(subString);
ensure(someString).matches(regex);

Similarly, ArraySubject supports a wide set of methods for array membership. Here’s just a few:

ensure(someArray).containsAllOf(elements...).inOrder();
ensure(someArray).containsExactly(elements...);
ensure(someArray).containsAnyOf(elements...);
ensure(someArray).containsNoneOf(elements...);

Note that when any of these assertions fail, it will not only tell you that it failed, but which array elements were the problem:

Expected [1, 2, 1, 2] to contain exactly elements [1, 2, 2, 1] in order, but contains only these elements in order: [1, 2]

You can also apply an assertion to each of the array elements:

ensure(someArray).eachElement().isGreaterThan(3);

Essentially what this last example does is to create a subject object for every array element, and then call the assertion function for that element.

In addition to the built-in subject types, there are also add-on packages which provide subject classes for DOM nodes, Selenium elements, and React-Enzyme wrapper objects.

It’s also fairly easy to extend Certainty to new types. There are two types of extensions: subject factory add-ons, and formatting add-ons. Subject factory allow new Subject classes to be registered for a given type. Formatting add-ons are used to tell Certainty how to print a given type of object. For example, it would be relatively easy to support something like Google protocol buffers or Immutable.js collections.

Certainty has comprehensive documentation. It also recently added support for TypeScript type definitions, although this is still in early development.

Certainty has been around for over a year, and (as of this writing) is used in about a dozen projects, both open-source and proprietary. I’d like to see it used more widely.

If you found this article useful, please recommend and share!