Jest is a simple testing library that works with zero configuration. Because of its great API - if you're already using Mocha - with a few find & replace and minimal effort, you can move to Jest without rewriting any of your tests.
In this post, I'll show you how I tweaked my existing Mocha setup to achieve this.
If you're new to Jest, don't forget to check out their Getting Started page as well.
I personally got hooked on this library when I realized it comes with a configuration option testEnvironment, that gives you a test environment with a JSDOM that works out of the box, without actually configuring JSDOM. I've lost count, across all projects, how many times I wanted to update the testing environment and ended up not doing it because I couldn't get JSDOM to work with my existing tests.
Let's jump straight into it!
Configuration
I use mocha.opts
to configure Mocha. The contents of this file are important because I'm going to reference them later:
--reporter spec
--ui bdd
--colors
--file test_helpers/babel_loader.js
--file test_helpers/jsdom_setup.js
--file test_helpers/test_setup.js
and this is how I used to run Mocha:
mocha --opts mocha.opts
You can configure Jest with jest.config.js
, a js or JSON file with the --config
flag, and in package.json
, see Configuring Jest. I suggest you start with package.json
because I think that's the simplest. Once the configuration gets more complex, you can always move it into a separate file.
In package.json
you should use the key "jest"
on the top level, like this:
{
"name": "my-project",
"jest": {
"verbose": true
}
}
now you should be able to run Jest as:
jest
Setup
This is the only place where depending on what other libraries you use in your tests, you might want to tweak their setup a bit.
In my case, test_helpers/test_setup.js
had the environment setup for Mocha.
If you're using Enzyme or Sinon for example, this is the place where you keep their configuration.
Let's say your file has something similar:
global.sinonSandbox = sinon.createSandbox();
afterEach(function() {
return global.sinonSandbox.restore();
});
Then you should be able to reuse this with Jest.
Jest does its environment setup with setupFilesAfterEnv
.
This option tells the library which file(s) to run before each test. Put this into package.json
after verbose
:
{
"name": "my-project",
"jest": {
"verbose": true,
"setupFilesAfterEnv": ["<rootDir>/test_helpers/test_setup.js"]
}
}
Side-note: this doesn't exactly match the behavior of the Mocha configuration. Instead of creating only one sandbox, now we create one sandbox before each test. However, there should be no difference in how Sinon interacts with your tests.
Hooks
Mocha operates with a wide variety of hooks. I mostly used describe()
, context()
, it()
, before()
, after()
, beforeEach()
, and afterEach()
.
Jest uses a bit different naming, in general, you're safe to replace:
before
➡️ beforeAll
after
➡️ afterAll
context
doesn't have an equivalent in Jest, so I decided to use describe
.
You can find & replace all occurrences of context
, before
, and after
or put the following code into test_helpers/test_setup.js
:
// mocha keyword mappings
window.context = describe;
window.before = beforeAll;
window.after = afterAll;
Function parameters
With Mocha, you were able to give hooks a description:
beforeEach(description, fn)
afterEach(description, fn)
in Jest, hooks only accept a function and an optional timeout:
beforeEach(fn, timeout)
afterEach(fn, timeout)
If your beforeEach
blocks looked something like this:
beforeEach('login', () => {
to strip out the descriptions from every beforeEach
run the following regular expression in your editor:
beforeEach\([^(]*
and replace it with
beforeEach(
Babel
We set up Babel in test_helpers/babel_loader.js
for Mocha:
// Run before any test files are loaded to use Babel for subsequent parsing
require('@babel/register')({
ignore: [/node_modules\//],
extensions: ['.js', '.jsx', '.ts', '.tsx'],
});
You have to install babel-jest
to use Babel with Jest:
yarn add --dev babel-jest
then add transfer
to package.json
that now should look like this:
{
"name": "my-project",
"jest": {
"verbose": true,
"setupFilesAfterEnv": ["<rootDir>/test_helpers/test_setup.js"],
"transform": {
"^.+\\.[t|j]sx?$": "babel-jest",
}
}
}
See more on using Babel with Jest.
At this point, you're pretty much ready to run your existing tests with Jest.
Conclusion
What I really liked about Jest is the "work great by default" philosophy.
Even when installed in an empty package and run, it gives you a meaningful output:
% jest
No tests found, exiting with code 1
Run with `--passWithNoTests` to exit with code 0
In /Users/akoskm/Projects/test
2 files checked.
testMatch: **/__tests__/**/*.[jt]s?(x), **/?(*.)+(spec|test).[tj]s?(x) - 0 matches
testPathIgnorePatterns: /node_modules/ - 2 matches
testRegex: - 0 matches
Pattern: - 0 matches
It doesn't ask for configuration files or necessary parameters, it simply works. I think this is a philosophy worth following when designing new tools.
What tools do you use to test your code?
Are you stuck in the middle of a migration? Let me know in the comments or find me on Twitter.