Mocking different values for the same module using Jest

Asís García
Trabe
Published in
3 min readNov 5, 2018
Photo by Sharon McCutcheon on Unsplash

Module mocks are a powerful tool to write unit tests with Jest. They allow you to isolate the code under test from its dependencies, leading to focused, less brittle tests. But, as many other powerful tools, module mocks can be tricky at times.

In this post we’ll explore how to mock different values for the same module in different tests.

Say you have a greetings module exporting a hello function which depends on another module to know the current language of the application. Something like this:

Mock me once, shame on you. Mock me twice… shame on you again?

Writing a unit test for hello involves mocking the lang dependency in order to control the current language:

You can use jest.mock (line 4) to mock the lang dependency. In the example above, the mock module has a current field which is set to a mock function. You want to test both branches of hello, so you use mockReturnValueOnce to make the mock function return "GL" in the first invocation, and"EN" in the second one.

You run jest, both tests pass, mission accomplished. You are a happy developer.

But wait. Try to focus the second test using it.only. Aw fish! Now the test fails:

Expected value to equal:
"Hi!"
Received:
"Ola!"

Well, it seems that the mock module setup is too brittle: you expect the mock function to be called in the same order you are defining it. That couples your test execution order to the mock setup, and that is… well, not good :)

Keep your friends close, and your mocks closer

There is a better way to setup a test like this one:

The key difference lies in lines 3, 13 and 20. You import the mocked module (line 3) to gain access to the mock function. Then, you call mockImplementation (lines 13 and 20) inside the test body to setup the right return value. Now you can use it.only whenever you want!

Mocking values, not functions

Suppose greetings changes: now it must use a different module to get the current language value. The new module is called appEnv and it exports the current language as a value. Now greetings looks like this:

You try and change the test accordingly:

You run jest again and… it fails! You get an error message:

greetings.test.js: "currentLanguage" is read-only

The problem is that you can’t assign a value to something you have imported. In the previous examples, you imported the mock function current, and you used mockImplementation to change its return value, but the imported value stayed the same¹. Now you can’t do that.

Don’t import, require

What you need is a way to use a different mock for each test. If you try something like this, you’ll still see a failing test:

In the previous code snippet, hello is imported before its dependency is mocked, so the tests are executed using the actual implementation of appEnv. It’s time to ditch all that ES6 fancy stuff. Just use a good ol’ require once you are done setting up the module mock:

Run the tests now… Still red, right? Well, you need to tell Jest to clear the module registry before each test, so each time you call require you get a fresh version of the required module.

This is the final, working code:

Summing up

We’ve seen how to mock a module to export different values for different tests. When the export is a function, you can mock it with jest.fn() and change its implementation for each test. When the export is a value, you need to go back to the basics and use require (and jest.resetModules) to ensure the order of execution doesn’t interfere with your mock setup².

¹ Well, technically it is the binding (not the value) what stays the same.

² You might want to take a look at jest.doMock if you want to change the mock value between two different assertions of the same test.

--

--