JavaScript testing #16. Snapshot testing with React, Jest, and Vitest

JavaScript React Testing

This entry is part 16 of 18 in the JavaScript testing tutorial

Unit testing is handy for testing how our React components behave when the user interacts with them. However, this might not be enough. Besides testing the logic behind a particular component, we should also test if our user interface changes unexpectedly. We can use snapshot testing to detect unintended modifications to how our components render.

Introduction to snapshot testing

Let’s create a straightforward React component so that we can test it. Its job is to render a user’s name and email.

UserProfile.tsx

In our test, we need to render our component and use the function. There are a few ways to do that.

The official Jest docs mention the react-test-renderer library that renders our component and provides its representation in JSON.

UserProfile.test.tsx

However, there is a high chance that you already use the React Testing Library, the most popular solution for testing React components. It provides the function, which returns a document fragment with the current state of our component’s rendered output.

UserProfile.test.tsx

How the snapshot is created

The first time we run our test, the function creates a snapshot file in the directory.

UserProfile.test.tsx.snap

If we’re using Jest instead of Vitest, the first line in the file will mention Jest.

The snapshot file contains the output of our React component at the time we called the function. We should commit the snapshot file to our repository and treat it as an integral part of our test.

Updating snapshots

Let’s modify our component slightly.

UserProfile.tsx

Above, we changed one of the paragraphs to . When we re-run our tests, the function compares the modified component with the snapshot we saved previously. This will fail because the rendered component does not match the snapshot.

When this happens, our testing framework will ask us if we want to update the snapshot. If we do, we will have to commit it to the repository again. We will have to do that every time we make changes to our code that affect how the component is rendered.

Avoiding non-deterministic tests

Let’s create a component that displays the current date.

CurrentDate.tsx

First, let’s write a simple snapshot test for it using the knowledge we already have.

CurrentDate.test.tsx

When we look at the snapshot file created, we can see that it contains the current date.

CurrentDate.test.tsx.snap

Unfortunately, this makes our test non-deterministic. If we run it tomorrow, it will fail. To deal with that, we should mock the current date. Fortunately, both Vitest and Jest support it.

If you want to know more about mocking, check out the following articles:
JavaScript testing #10. Advanced mocking with Jest and React Testing Library
JavaScript testing #13. Mocking a REST API with the Mock Service Worker

CurrentDate.test.tsx

When using Jest, use and instead.

Thanks to the above approach, our test will always use the same date, no matter when we run it.

Loading data asynchronously

Let’s create a simple component fetching a list of posts from a REST API.

Posts.tsx

The custom hook loads the data asynchronously through the hook.

usePosts.tsx

It uses the function under the hood to fetch the data.

fetchPosts.tsx

Thanks to that, we can easily mock the function.

First, let’s create a snapshot when the posts are still loading.

Posts.test.tsx

Second, let’s create a second snapshot when the posts are already loaded. In this case, calling the function at the right moment is crucial. We need to wait for the posts to be loaded.

Posts.test.tsx

Running this test suite creates a snapshot file that contains two snapshots.

Posts.test.tsx.snap

Summary

In this article, we’ve discussed snapshot tests and implemented them in simple and more advanced scenarios that involved mocking and asynchronous data loading.

Snapshot testing can help ensure that the user interface does not change unexpectedly. It can also come in handy in situations where we have complex components and tracking the changes manually would be especially difficult. Therefore, incorporating snapshot testing can make our codebase more reliable and maintainable by making it easier to catch any unintended changes early in the development process.

Series Navigation<< JavaScript testing #15. Interpreting the code coverage metricJavaScript testing #17. Introduction to End-to-End testing with Playwright >>
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments