Test Patterns With React Hooks
By: Luis Paredes
Tags: Jest, Enzyme, Web Development, React Hook, React

Writing components using React has become a cleaner and more efficient task since the introduction of the Hooks API.

However, adopting this new API brings about a paradigm shift: instead of using classes with their attributes and methods, now we have to deal with regular functions.

Test Patterns Before Hooks

Before the introduction of Hooks in React 16.8, state changes were only possible using class components, and as a result of that, developers made use of the possibility to access class-component methods directly, when writing unit tests:

However, this pattern can’t be used if the component is a plain function, and since Hooks-based components happen to be plain functions, we have to find alternatives to test state changes in this type of components.

Possible Solutions

The official documentation recommends several alternatives which, theoretically, would allow us to test state changes in our components in an effective and efficient way.

Nevertheless, the suggestions from the documentation didn’t provide me with the level of efficiency and effectiveness I was looking for, mostly because I found them too verbose and because the given recipes seemed too simple when compared with the things I needed to test.

Because of this, I decided to invest a little more time researching about the topic. After a lot of reading and experimentation, I started noticing some patterns:

Test Patterns

If there are functions inside the component not making use of names of the closure, they can be refactored into separate functions outside the component to be tested as single units, e.g.:

2. If there are functions inside the component only using a few names of the closure, they can also be refactored into separate functions that take those names as arguments:

3. If finding a selector for an element that triggers an action is too cumbersome (e.g. the trigger is several levels of abstraction inside the component being tested), just add an id or a class that identifies the trigger unequivocally.

4. If you want to test a block of code is reached within a conditional, find a function inside that block that can be mocked and use expect(mockedFunction).toHaveBeenCalled() :

5. If what you need to test involves a UI update to an inner component, then you can simply verify the inner component prop that is linked to the update has changed its value:

6. If the expected UI change involves mounting/unmounting a particular component, you can use wrapper.contains(<ComponentThatGetsMountedAndUnmounted/>) (with Enzyme):

7. [Bonus] In some cases, it’s possible to mock the Hooks directly and check they were called with the expected values (credit: @agiveygives):

Conclusion

In summary:

The new test style revolves around features instead of logical units.

The universal principle of “divide and conquer” is also very useful in this case, since it’s easier to test several tiny blocks of code one by one, than to test an entire wall of code with a single test.

Some of these patterns are analogous to the ones used with class components, but instead of calling a class method directly, you have to simulate an event.

There are patterns used for testing class components that can be reused with function components without any modifications. Snapshot testing is an example of this type of pattern.

If you have any doubts, suggestions, or if you’ve stumbled upon other patterns, don’t hesitate to leave them in the comments!