Combining Chai and Jest matchers

In this post I am going to show how to combine both Chai and Jest matchers in the same codebase. This can help migrating existing Mocha/Chai tests to Jest.

Jest logo

Since Jest 14 released snapshot testing I have been wanting to use snapshot testing with my existing Mocha codebase which was using Chai matchers. First I was going to use a library to do this and I submitted a talk about this to the ReactNL conference.

Having submitted the talk I got contacted by Christoph Pojer (who develops Jest) and he asked me to consider migrating to Jest instead of using a snapshot testing library. Jest has snapshot testing integrated in the CLI and it could also work with my existing Webpack setup. To give a full picture in my talk I figured I should at least see how difficult it is to migrate. During the process I began to appreciate the nice integration of snapshot testing as well as the powerful mocking features in Jest. I also realized that our current testing setup using mocha-webpack was slow and unpredictable. So I decided to do the migration for real on our production codebase.

However, long technical migrations are not much fun and don’t deliver any business value. Therefore I don’t want to have to convert all my existing assertions at once. Especially since we’re also using some Chai plugins. These don’t map directly to the standard matchers in Jest. We had around 700 tests to migrate in 120 test suites.

In the most basic case you could alias the global expect to something like “jestExpect”. However, this was not working due to some internals of Jest. After some discussing I fixed this with a PR. This was released in Jest 16.

Now it is possible to use Jest and Chai together by aliasing the Jest global “expect”:

This code can be put in the setupTestFramework script. Now you can write code like so:

This works, is easy to implement and explicitly shows which assertion standard you are using. If it doesn’t bother you that there are two ways to do expectations you can stop reading.

For some reason it did bother me and after some tinkering I discovered it is also possible to get it to work like this:

We can do both Jest and Chai expectations with the same global “expect”! A big advantage of developing for unit testing is that is easy to do in a test-driven approach. However, when my test failed I still had to get into the NodeJS debugger to investigate what was going on internally.

Getting this to work is difficult because accessing the “not” property of Chai causes a flag to be set which negates the whole expectation. Also the “not” property is read-only while we need to write to it to merge it with the “not” property of the Jest expectations.

But as always, a snippet of code says more than a thousand words:

This shows that due to the dynamic nature of JavaScript you can do some powerful monkeypatching. Put that in your setupTestFramework script and it will work as expected.

Now I can finally write the expectation this all started with:

Consulting Full Stack Developer @Xebia | Frontend Expert

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store