On Using `assert` as Your Assertion Library

Colin J. Ihrig

This post is a more detailed followup to this tweet in which I advocate for using Node's assert module as your goto assertion library.

Disclaimer: There are plenty of perfectly good JavaScript assertion libraries out there. If you're happy with your current choice in assertion libraries, please continue using them. This post is just my personal opinion. It does not represent the opinion of my employer, your employer, or anyone else.

Background

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:

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.

So, why assert?

  1. 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 assert for all new code, and generally migrate legacy code as I update it for other reasons.
  2. assert is 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 assert is actively maintained as part of the Node runtime means that support for new language features generally shows up fairly quickly.
  3. assert has a simple API. Libraries like chai and @hapi/code tend 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 assert API can be more or less memorized fairly easily.
  4. It's pretty hard to write incomplete assertions with assert. Remember earlier when I said that @hapi/code was created because of chai's incomplete assertion problem? Remember how I said that @hapi/code has 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!
  5. assert has access to more powerful APIs. As part of the Node.js runtime, assert can (and does) reach into the C++ binding layer and get information directly from V8. This is particularly useful in a language like JavaScript that makes it very hard to determine a variable's real data type. Userland libraries have to either guess, build on top of assert, or access the binding layer, which is dangerous, not supported, and going away in future versions of Node.

Conclusion

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.