Migrating from Jest to Mocha (Part 1)
This post is the beginning of a series on moving from the Jest test runner to Mocha. In this post, I’ll look at the main pieces that need to be changed.
There are many reasons why you might want to replace Jest with Mocha.
For me, the main driver is getting one step out of the Facebook ecosystem. I found ths Jest architecture video fairly shocking–almost the definition of accidental complexity. Years ago, I used Jasmine as a test runner and it was perfectly fine. Why can’t a test runner be a straightforward piece of software that changes little?
The plan
Here’s the various pieces that need to be worked on:
- Replace the
jest
andjest-environment-jsdom
packages withmocha
,chai
,sinon
, andchai-sinon
- Configure
mocha
to run withbabel
, transpiling to ES5 - Replace expectations with the
chai
expect syntax - Replace non-componenent mocks with
sinon
equivalents - Replace component mocks (
jest.mock
) with Rewire (or something else)
The first five items should be straightforward; the sixth will be a struggle. Let’s briefly look at that before we get on to the easier stuff.
Mocking components with jest.mock
One of the most useful features of Jest is jest.mock
, which allows us to stub out child components that do things we don’t care about, perhaps because they have side effects that run on mount.
import { MyComponent } from "./MyComponent";
jest.mock("./MyComponent", () => ({
MyComponent: jest.fn(() => <div id="MyComponent" />),
}));
Jest does this by using Babel to hoist the jest.mock
call before the import, and then rewiring all imports that match the file path (/.MyComponent
in this example) to use the values returned by the jest.mock
implementation. All the keys of the created object (just MyComponent
in this example) become exports in the mocked module.
Because of the hoisting, you can’t use any variables within the test module. You can however use JSX, which Babel will also transpile to calls to React.createElement
.
As I covered in the post on alternatives to module mocks, this isn’t the only way to mock modules. Node’s CommonJS support means that module objects are shared between all usages, so you can actually overwrite instances if you have access to them.
That technique will never work with ECMAScript 6 modules so I think it’s better to sunset use of that and find alternatives.
There are a couple of packages that may help us: https://github.com/jhnns/rewire and https://github.com/speedskater/babel-plugin-rewire.
But we’ll come back to that….
Jest packages vs Mocha packages
A little bit of a detour first. If I run the following in a blank directory:
npm install --save-dev jest jest-environment-jsdom
I get:
% npm install --save-dev jest jest-environment-jsdom
added 339 packages, and audited 340 packages in 14s
And if I do:
npm install --save-dev mocha jsdom jsdom-global chai sinon-chai
````
I get:
bash % npm install –save-dev mocha jsdom jsdom-global chai sinon-chai
added 161 packages, and audited 162 packages in 3s
So... the Mocha environment has about half the number of packages. Interesting!
## Replacing Jest packages with Mocha
Okay, let's do it: I've checkout out the source for the second edition of [_Mastering React Test-Driven Development_](https://github.com/PacktPublishing/Mastering-React-Test-Driven-Development-Second-Edition) and made a copy of the directory `Chapter18/Complete`, naming it `Chapter18/Mocha`.
Next, I've run:
bash npm uninstall –save-dev jest jest-environment-jsdom npm install –save-dev mocha jsdom jsdom-global chai sinon-chai
Easy! But does it work?
bash Chapter18/Mocha % npx mocha
/Users/daniel/work/react-tdd/Chapter18/Mocha/test/AnimatedLine.test.js:1 import React from “react”; ^^^^^^
SyntaxError: Cannot use import statement outside a module ```
No, it does not.
In the next part of this series, we’ll configure Mocha to use Babel to transpile our ES6 tests to ES5.
— Written by Daniel Irvine on August 26, 2022.