Resilient Snapshot Testing with Material-UI and React Testing Library
Tags: react, jest, materialui
Published: 2021-09-03 17:22:30

---
title: Resilient Snapshot Testing with Material-UI and React Testing Library
published: true
description: If you’re using Material-UI for snapshot testing, you already know that it can create a surprising amount of unnecessary snapshot updates. In this post, we’ll show you how to easily modify the behavior of Material-UI’s CSS-in-JS solution to cut down on the noise in your snapshots!
tags: react, jest, materialui
canonical_url: https://medium.com/building-the-open-data-stack/resilient-snapshot-testing-with-material-ui-and-react-testing-library-befa04d4446f
cover_image: https://miro.medium.com/max/1400/1*4ekcyIR2TjMFIoHFhXMyXA.jpeg
---

_This article was [originally published](https://medium.com/building-the-open-data-stack/resilient-snapshot-testing-with-material-ui-and-react-testing-library-befa04d4446f) on the [DataStax Tech Blog](https://dtsx.io/3ytZJJL)_

---

_When appropriately used, snapshot testing can be a fantastic way to cover component interactions in your test suite. With just a few lines of test code, developers can validate hard-to-test component behavior in a low-cost manner. However, snapshot testing works best when they fail as a direct cause of meaningful changes to the component’s behavior under test. This style of testing becomes much less helpful if snapshots are volatile and regularly track superficial changes to your components._

Using snapshot testing with a library that uses the popular [Material-UI](https://material-ui.com/) component library can create a surprising amount of unnecessary snapshot updates and effectively remove snapshot testing as a valuable tool in your testing toolbox. This article will examine this problem and find a solution to cut down on this extra snapshot noise.

> *Note:* This writeup assumes that you are using Jest, Testing Library (React), and Material-UI in your project.

---

## The Problem

Snapshot tests become much less useful when paired with MaterialUI’s CSS-in-JS solution. At runtime, MaterialUI’s [StylesProvider](https://material-ui.com/styles/api/#stylesprovider) guarantees globally unique class names for your app by adding incremental IDs to the end of the generated class names. This class generation method leads to frivolous snapshot updates like the example below:

```diff
<div>
   <div
- class=”makeStyles-wrapper-1"
+ class=”makeStyles-wrapper-2"
   >
```

Snapshot diffs like this one increase the signal-to-noise ratio of snapshot changes and water down their usefulness. Developers cannot hunt for the meaningful differences in snapshot tests, leading to an increase in bugs introduced to the system.

## Solution: Cut down on the className noise
Fortunately, we can modify the behavior of Material-UI’s CSS-in-JS solution and reduce the noise in our snapshots by:

1. Modifying Material-UI’s class generation function to drop the unique identifiers at the end of class names
1. Creating a custom render function for React Testing Library using the modified generator function
1. Using our custom render function in place of the base render function for all of our tests
1. Updating all existing snapshots to drop the generated class noise.Modifying the class generator

### Modifying the Class Generator

Material-UI uses a [StylesProvider](https://material-ui.com/styles/api/#stylesprovider) component to manage the style-related context in our application. This component has a `generateClassName` prop that allows us to pass in a custom function telling the provider how to construct new class names when needed. We can wire up a function that drops the unique ID that affects our snapshots:

```js
const snapshotFriendlyClassNameGenerator = (rule, styleSheet) =>
   `${styleSheet.options.classNamePrefix}-${rule.key}`;
```

We want to keep our `snapshotFriendlyClassNameGenerator` as close to our running app as possible, so we retain any prefix or rule key that might be present. This leads to class names like `makeStyles-wrapper` but without any numbered identifier as a suffix. We can now pair our custom generator function with an instance of `StylesProvider`:

```jsx
const SnapshotFriendlyStylesProvider = ({ children }) => 
   <StylesProvider generateClassName  
      {snapshotFriendlyClassNameGenerator}>
      {children}
   </StylesProvider>
);
```

### Create a custom render function

In order to introduce our new `SnapshotFriendlyStylesProvider` into all of our tests, we need to write a custom React Testing Library render function like so:

```js
const customRender = (ui, options) =>
   render(ui, {
      wrapper: SnapshotFriendlyStylesProvider,
      …options,
   });
```

The subject of custom render functions is not new. The [official docs](https://testing-library.com/docs/) have a [great write-up](https://testing-library.com/docs/react-testing-library/setup#custom-render) about why you might need one and how to implement one. In a nutshell, we are just wrapping a regular render call in our new `SnapshotFriendlyStylesProvider` to remove additional class name noise!

## Using our custom render function

To see the payoff we want, we need to use our new `customRender` function instead of the render function provided by React Testing Library. Next, we need to create a `testUtils.js` file and re-export the rest of React testing library.

```js
export * from “@testing-library/react”;
// Override our render with the snapshot-friendly render.
export { customRender as render };
```

A final `testUtils.js` file with all of the previous steps may look like this:

```jsx
import { render } from “@testing-library/react”;
import { StylesProvider } from “@material-ui/core”;

const snapshotFriendlyClassNameGenerator = (rule, styleSheet) =>
   `${styleSheet.options.classNamePrefix}-${rule.key}`;
const SnapshotFriendlyStylesProvider = ({ children }) => (         <StylesProvider 
   generateClassName{snapshotFriendlyClassNameGenerator}>
   {children}
</StylesProvider>
);

const customRender = (ui, options) =>
   render(ui, {
      wrapper: SnapshotFriendlyStylesProvider,
      ...options,
   });

export * from "@testing-library/react";
// Override our render with the snapshot-friendly render.
export { customRender as render };
```

### Finish the job

To complete the change and bring some more resiliency to your snapshots, we need to perform the final step of utilizing our `customRender` function instead of the out-of-the-box render function provided by React Testing Library and re-generate all of our snapshots. Hence, future changes to our tests generate relevant, slimmed-down snapshot diffs.

To use our new render function, we can update all of our tests as follows (assuming `testUtils.js` and our target test is in the same directory):

```diff
- import { render } from ‘@testing-library/react’;
+ import { render } from ‘./testUtils’;
```

Finally, we can update all of our test snapshots.

> *Warning:* Make sure you are making this change in isolation; you do not want to accidentally overlook a separate issue that might get buried in the snapshot diffs generated by this step!

```sh
# using jest directly
$ jest — updateSnapshot
# create-react-app/react-scripts example
$ npm test — — updateSnapshot — watchAll=false
```

After this point, all future snapshot tests should not have frivolous style-only diffs for your Material-UI components. Huzzah!

## Wrapping Up

By reducing the noise generated by Material-UI’s class names, we can regain the use of snapshot tests in our codebase. We also now know how to construct custom render methods for our tests, allowing us to cut down on boilerplate code in our tests. Finally, we also now have an excellent foundation for future reusable test utilities that we can use to make our tests easier to write and clearer to read.

_Follow the [DataStax Tech Blog](https://dtsx.io/3ytZJJL) for more developer stories. Check out our [YouTube](https://dtsx.io/3yoWOSP) channel for tutorials and here for DataStax Developers on [Twitter](https://dtsx.io/2WquUck) for the latest news about our developer community._

## References

- [Material-UI’s official docs](https://material-ui.com/)
- [Testing Library — React](https://testing-library.com/)
- [Creating custom render functions in React testing library](https://testing-library.com/docs/react-testing-library/setup#custom-render)
- [Relevant bug report on Material-UI repository](https://github.com/mui-org/material-ui/issues/9492)