On Using `assert` as Your Assertion Library
By Colin J. Ihrig at
Node.js ships with its own assertion module,
assert. This module was created to serve Node's own
internal testing purposes. Publicly exposing
assert was largely considered to be a mistake, but
Node was unable to remove it from the public API because the ecosystem had come to rely on it. Instead,
the API was considered "locked" and the documentation actively discouraged its use. That all changed in
early 2017, and
assert is actively developed and maintained.
While all of this was going on, several popular userland assertion libraries came into existence. One
of the most popular is
chai. It exposes several styles of assertion
APIs, including one that is very much like
assert.notStrictEqual(3, '3', 'no coercion for strict equality');
Another style allows you to write assertions that almost resemble natural language:
expect([1, 2]).to.not.equal([1, 2]);
However, a problem was noted with some of
chai's assertions. Using
chai, it was easy to write incomplete
assertions by forgetting to add
() to function calls. This lead to the creation of
@hapi/code, a more minimal assertion library created specifically
for the hapi framework. It removed the use of getters, and required that assertions be proper function calls.
It even provided APIs to detect incomplete assertions. These APIs are consumed by hapi's test runner,
@hapi/lab to report such errors during testing.
For a bit of additional context, I was the lead maintainer of
@hapi/code for approximately four years.
- Personal preference. If you have a personal preference to use another library, then continue to do so. I don't recommend revisiting every test you've ever written just to change assertion libraries. I still have a lot of tests that use
@hapi/code, but I've moved to
assertfor all new code, and generally migrate legacy code as I update it for other reasons.
assertis built into the Node.js platform, and actively maintained. This reduces the need for an additional dependency. This leads to simpler projects, and removes a security vulnerability vector. The fact that
assertis actively maintained as part of the Node runtime means that support for new language features generally shows up fairly quickly.
asserthas a simple API. Libraries like
@hapi/codetend to do a lot of magic under the hood. In 2014, I thought it was really cool to be able write assertions like English sentences. However, by 2018, I got tired of checking the documentation (for a library that I was maintaining!) to remember how some of the magic assertions worked, and how their behavior changed depending on if they were operating on objects vs. arrays vs. strings vs. primitives, etc. By comparison, the
assertAPI can be more or less memorized fairly easily.
- It's pretty hard to write incomplete assertions with
assert. Remember earlier when I said that
@hapi/codewas created because of
chai's incomplete assertion problem? Remember how I said that
@hapi/codehas APIs for detecting incomplete assertions? You have to try pretty hard to write an incomplete assertion with
assert, and therefore, you don't need APIs to detect them. Simplicity is a great thing!
asserthas access to more powerful APIs. As part of the Node.js runtime,
assert, or access the binding layer, which is dangerous, not supported, and going away in future versions of Node.
I hope you found this quick analysis useful. Again, if you're happy with your current assertion library, then keep using it. As the former maintainer of a userland assertion library, I have personally moved to using
assert in new code, and have been happy with the decision.