Simpler Jest mocking

There are a lot of ways to mock things in jest. And that's not counting spies, which are like mocks, but different.

I still probably haven't read all the mocking-related Jest docs. Why would I do that? Anyway, the docs describe the preferred way to mock, and it's super simple:

If the module you are mocking is a Node module (e.g.: lodash), the mock should be placed in the mocks directory adjacent to node_modules

Nice. I've used this feature a lot. For stuff like this:

// ./__mocks__/axios.js
import mockAxios from "jest-mock-axios";
export default mockAxios;

Now when I import axios from 'axios' in a test file... I'm not actually importing it. I'm importing the mock.

On the one hand, this is really nice. I can write one mock file, and now anytime I'm in a test environment, axios is automatically mocked for me, and I've got some custom test APIs I can run. 🆒

But it turns out this is kind of a pain in the ass sometimes! Specifically:

  1. If you don't want to mock the module, or
  2. if you want to mock the module differently, or
  3. you forgot that you mocked the module in the first place.

The thing that sucks about this is that, in the context of your text file, the mock is implicit; there's no indication that module is mocked. When you look at the file for the first time, or come back to it after not having looked at it in a while, and see this:

// my.test.tsx
import axios from "axios";

...you're probably going to assume that that file has imported the axios module. But it hasn't!

This can get even worse if you're mocking your own modules, where the docs recommend this:

Manual mocks are defined by writing a module in a mocks/ subdirectory immediately adjacent to the module. For example, to mock a module called user in the models directory, create a file called user.js and put it in the models/mocks directory. Note that the mocks folder is case-sensitive, so naming the directory MOCKS will break on some systems.

Do this an you'll end up with a bunch of __mocks__ directories all over your codebase.

You may, as I have, come to regret this choice, especially if you've already written a lot of tests. Read the docs. There's a simpler way:

// my.test.ts
import axios from "axios";

jest.mock("axios");

test("example test with a mock", () => {
expect(axios).toHaveBeenCalled();
});

The nice thing about this is that the mock is explicit! It says .mock right there in the file! And that's all you need to do. No magic. Mocks are inherently weird and confusing—avoid them if you can—because the code you import is not really the code that gets tested. Sort of!

Keep it simple, that's all.

Credit to this post, which set me straight a little bit.