Alternatives to module mocks in your unit tests

The jest.mock magic function is my default way of mocking components, but there are a couple of other approaches.

One is shallow rendering, wich uses React’s built-in shallow renderer which renders just the one parent component you give it, but none of the child components it contains. That way you can test all the standard HTML elements that you want rendered.

I find this less useful because:

Despite that, if you want to see an example of it, take a look at the first edition of Mastering React Test-Driven Developmnet, within the file test/App.test.js. (In the second edition of the book I no longer use shallow rendering at all.)

The other thing you can do is rely on Node’s CommonJS exports being shared objects. If you can get a hold of a reference to the module object, then you can modify its exported constants in your tests, which then changes the object your application code sees. For this, you needed to use a namespace import.

You can find an example of this in the repo for the first edition of Mastering React Test-Driven Development, within the file test/AppointmentFormLoader.test.js. Note, I don’t recommend using this approach any more (see below):

// test/AppointmentFormatLoader.test.js
import * as AppointmentFormExports from '../src/AppointmentForm';
AppointmentFormExports.AppointmentForm = jest.fn(() => null);

// src/AppointmentFormLoader.js
import { AppointmentForm } from './AppointmentForm';
// above is imported as normal. The test rewrites the object value

The reason why I no longer use this is that although it looks like ES6, it’s not. It’s relying on Babel’s transform of this into CommonJS using require imports. In ES6, namespace exports are not modifiable. This pattern won’t work.

Unfortunately, jest.mock doesn’t work on ES6 either! However, the Jest team are working on it: see this GitHub issue. Chances are, there will be a similar API for ES modules soon.

— Written by Daniel Irvine on August 26, 2022.