Mocking modules with Jest
— 2 min read
Jest is a great tool for adding confidence in our codebase, primarily by unit testing our implementation. It provides a wide range of functionality to simplify the tests we write.
A common case when unit testing our modules, is mocking external dependencies of tested modules. This is where
jest.mock(...) comes in the scene by doing exactly that. This is great for globally mocking these dependencies in our testing file.
Something that is not really straight forward is how to manually mock module dependencies for each separate test.
To better illustrate this, imagine having a module
A.js depending on another module
B.js as follows:
There are different approaches to mocking module dependencies. Let's see some of them based on the example above and when each of these is the best option. But first lets start with the case when we don't mock any external dependency.
When we don't apply any mock for the someFunctionB function, the actual function is getting triggered. That way, our test depends on the result of the actual implementation of that function. We don't really want to keep this behaviour for unit testing our module. This would be a good practice in case we wanted to test the result of integrating those two modules.
Global mock (default)
By using jest.mock for the dependencies of the tested module, Jest will enable auto-mocking for the dependency module. In that case, the module implementation is replaced by default mocks. In this specific example
someFunctionB from auto-mocked module
B.js will be replaced with a
jest.fn() which returns an
undefined value. You can find more information about auto-mocking Jest feature here.
Global mock (explicit implementation)
There are cases when Jest auto-mocking feature is not enough, and we need to explicitly mock the implementation of the module. The above approach is good enough as far as the mocked implementation covers all our test cases. But what happens when we need an explicit mocked implementation for each of our tests?
To explicitly test our module based on specific module dependency results, we need to:
auto-mock the module imported
import the function we need to mock from that module
in each of the depending tests
3.1. mock the function with an explicit mocked implementation
3.2. clear the mock for not affecting next tests
To add a mock function implementation for each of our tests we are using
mockImplementation. We explicitly define the dependent module implementation and its return value. This will also affect the result of the actual tested module. As result, we can test our module based on all the possible result combinations of its dependencies. You can learn more about
A common pitfall is that our mocks need to be in a clean state after each test is run. As a result, we need to clear these mocks at the end of each test, so as our tests are not affected from one another.
As we can see there is not a silver bullet of mocking a dependency of a module while unit testing it. Each mocking strategy above covers a different test case. We should be aware of what we want our tests to achieve and select the best option based on these needs.