JavaScript testing #10. Advanced mocking with Jest and React Testing Library
source link: https://wanago.io/2022/04/18/advanced-mocking-jest-react-testing-library/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
In the fourth part of this series, we’ve learned the basics of mocking API calls. However, there are often situations where we would like to test various more demanding cases. So, in this article, we implement more advanced examples on how to mock with Jest.
Creating a simple React component
Let’s start by creating a straightforward React component that renders a list of posts.
Posts.tsxThe logic of storing the data in the state resides in the usePostsLoading hook.
usePostsLoading.tsxThe fetchPosts function takes care of fetching the data. It is the one we will mock in this article.
fetchPosts.tsxMocking a function
In the fourth part of this series, we’ve defined mocks in the __mocks__ directory. Instead, we can mock a function at the top of our test file.
Mocking a named export
Let’s rewrite our fetchPosts.tsx file to use a named export for a moment.
fetchPosts.tsxWith the above approach, it is straightforward to mock a module.
Above, we use jest.mock() function that creates a mock function. For example, we can use it to change the value that a function returns. In our case, we force the fetchPosts function to return a promise that resolves to an empty array.
Mocking a default export
Our original fetchPosts.tsx file uses a default export. Therefore, we need to modify our mock a bit.
We can simplify the above mock a little by using the mockResolvedValue method.
Posts.test.tsxThe fetchPosts returns a promise that resolves to an empty array thanks to the above. Because of that, the Posts component does not make an actual HTTP request.
A crucial thing to notice in our test is that our fetchPosts function is still asynchronous. Since we use await posts.findByTestId('posts-empty'), our test waits up to 1000ms for the <div data-testid="posts-empty" /> to appear. If the React Testing Library does not find the element during that time, it throws an error, and our test fails.
Mocking a function differently per test
Above, we create a mocked version of the fetchPosts function that always returns the same value. Instead, we might want to mock a function differently per test. Let’s start by using the jest.mock() function on top of our test.
Thanks to doing the above, our fetchPosts function is now replaced with a mock function. The crucial thing to acknowledge is that when we import fetchPosts in usePostsLoading.tsx or post.test.tsx, we import an instance of a mock function. So we can use this fact and interact with our mock through the test to change what it returns in usePostsLoading.tsx.
Posts.test.tsxSince the fetchPosts function that we import on top of our test now has a value of jest.fn(), we can call mockResolvedValue and mockRejectedValue on it. We can achieve a different mocked value per test by manipulating the fetchPosts mock before each test.
Mocking a React component
When writing tests with Enzyme, we can choose to either render a component with all of its children or perform a shallow render. In contrast, we don’t have this option with React Testing Library.
The idea behind React Testing Library is to write tests that resemble the way the users interact with our application. Because of that, mocking React components is discouraged. Even though that’s the case, we sometimes might want to avoid testing some components as a whole. The above might happen when using third-party libraries, for example.
DataViewer.tsxIn our test, let’s find out if the DataViewer renders the ReactJson component when provided with a dictionary.
DataViewer.test.tsxWithout mocking the react-json-view library above, we wouldn’t have a straightforward way of checking whether our component rendered it when using React Testing Library.
Mocking a module partially
So far, we’ve constantly mocked a whole module when using jest.mock(). We might not want to do that in every case. Let’s imagine having the following file:
utilities.tsxIf we want to mock only the sum function, we can pair jest.mock() with jest.requireActual(). When we call jest.requireActual(), we can retrieve the original content of a module even if we mocked it.
Summary
In this article, we’ve gone through various use-cases of mocking functions. This included real-life situations such as mocking a function differently per test, mocking a default export, or mocking a React component. We’ve also learned how to mock a module partially. All of the above provides a cheat sheet that might come in handy in various cases.
Series Navigation<< JavaScript testing #9. Replacing Enzyme with React Testing LibraryRecommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK