Advanced React Component Mocks with Jest and React Testing Library
I am a huge fan of Jest and React Testing Library. When learning these tools, something that occasionally trips up new users is figuring out what to do with complex external dependencies. Your components will often import others, either your own or third-parties’, and ensuring those will render correctly sometimes places an undue burden on your unit tests.
Fortunately, it is trivial to use mocks and ignore the complexities of other components when you’re testing. This is actually a Good Thing, by testing only the behavior of a specific unit of code (in this case, a component) and mocking the behavior of dependencies you isolate your tests, minimize complexity, and rely on other tests to verify other components.
Simple Inline Component Mocks
Consider a simple component that renders some text and a custom component called MyComponent
Imagine MyComponent
does more than we care to manage for a simple unit test, like access local storage or make API requests. We know MyComponent
has tests that verify its behavior, and to test our App component we only want to check that it renders correctly. Using Jest Mocks, you can quickly create a functional component to stand in for MyComponent
in your test:
Manual Mocks
If your mock is more complex, you might decide to use a manual mock. To do so, make a new directory called ‘__mocks__’ next to the component you want to mock. Remove the factory argument from your call to jest.mock, and jest will automatically locate manual mocks and use them in your test.
This is useful if you always want your mock to behave the same way. Recently, however, I needed to create a mock that behaved differently in different unit tests, and needed to use some advanced features of React as well.
Advanced Component Mocks
Lets say for the sake of example that you don’t want to use a manual mock (the mock behavior you are setting up is not constant for all your tests) and you need to use some other dependency in your mock, like a React hook.
(I am about to go through a couple of wrong approaches to solving this first, feel free to scroll to the bottom if you just want a working example!)
Using an inline mock, you make a call to React.useEffect()
And you get an error!
The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
If you research this error a bit, you will find jest factory methods are only allowed to access a whitelisted array of object, or objects that are begin with the word ‘Mock’.
The logical solution then is to name that anonymous function, MockMyComponent.
Another error!
ReferenceError: Cannot access 'MockMyComponent' before initialization
This one seems especially weird, since MockMyComponent
is clearly defined before it is used. What is going on? The answer is Jest hoists mocks to the top of your module’s scope. This is how a call to jest.mock()
can come after the mocked module is imported but the mock still works.
The answer is to use jest to set up up your mock, then before you run your tests, change the implementation of your Mock to your functional component:
Note that because MyModule
is a default export, we set up the jest factory mock with some additional data so everything behaves correctly.