Jest is an industry-standard testing framework for JS projects. It was developed by Facebook to test their code and was made open-source. In this article, I will teach you to create custom Jest matchers to supercharge your testing skills.
What is a matcher?
A matcher (or an assertion) is a function that is used to check for a specific condition. Most often, it compares two values. Here are some of the basic matches available in Jest:
Why do we need custom matchers?
While Jest is very powerful out-of-box, there is always something specific to the project you can add. It should improve readability and reduce the amount of code by creating a custom Jest matcher for a common use case.
In one of my projects, I had to test whether a certain value was a correctly formated ISO date. It can be done with a regular expression, but I had to test it so many times, it was worth moving this logic to a custom Jest matcher.
Add a setup file
Before we add the matcher itself, it is important to add a setup file for Jest. A setup file is a file that is used to set up the environment and do things like add custom matchers, enable mocks and configure jest. Create a file called setupJest.js
in the project root, this will be our set up file. Now you need to add this line to your Jest config (in package.json
or jest.config.js
):
"setupFilesAfterEnv": ["./setupJest.js"]
Note for React Native/Expo users: if you are using the jest-expo preset, this would not work. The problem is that the preset overwrites the setupFilesAfterEnv
setting. To circumvent it, write your config like this:
Now, you overwrite the preset setupFilesAfterEnv
setting and add your own file.
Write a custom Jest matcher
In this example, I am going to write a matcher that will check if a string is a correctly formatted ISO date. This is done using the expect.extend
function. It accepts an object whose keys are matcher names, and values are functions that perform the actual matching. Here is the code that goes to setupJest.js
You can see that toBeISODate
is a function that accepts a single value, received
. You can accept more arguments, these will be passed to the matcher directly (more on that later). On lines 4-6 we check received
against the regexp and on lines 14-15 we check if JS can correctly parse received
.
The matcher has to return an object with two properties: pass
and message
. pass
is easy: true if the checks pass and false if not. message
is a little more confusing. When pass=false
, message
is the error that will be shown if expect(something).toBeISODate()
fails. When pass=true
, message
is the error that will be shown if expect(something).not.toBeISODate()
fails. It is never shown if the test passes. Now you can use this matcher like this:
const timestamp = get_timestamp_from_somewhere_unreliable();
expect(timestamp).toBeISODate();
Accept more arguments
The toBeISODate()
matcher does not accept any additional arguments and it does not need to. But, suppose we want to write a matcher that checks if a number is a power of some other number. Here is the code for such a matcher, toBePowerOf()
:
It accepts one more argument, power
. You can use this matcher in your code like this:
expect(8).toBePowerOf(2); // passes
expect(10).toBePowerOf(3); //fails
expect('abacaba').toBePowerOf(); // throws
Thank you for reading this article, I hope you found it helpful. Check out my other articles on JavaScript:
- Improve your Redux skills by writing custom middleware
- React.useMemo and when you should use it
- ReasonML: a better alternative to TypeScript
THANK YOU. I could not find a solution to the jest-expo preset overriding my setupFiles definition, but saw plenty of devs running into the same issue. This solves it 100%.