Devil in Disguise: Jest Snapshot Testing

Why snapshot tests should be used with caution

Photo by Hoil Ryu on Unsplash

Snapshot testing is a double-edge sword. It could be a great tool if you use it according to the guideline recommended by Jest. At the same time, most developers tends to avoid it at all cost. So do I. In this article, I’ll give a few examples of why .toMatchSnapshot() brings more harm than good.

Tests become harder to read

Tests are not only meant to run in continuous integration (CI) pipeline but also to be read by humans. Good tests serve as the living documentation of your code. What would you understand from the following test?

import renderer from 'react-test-renderer';

import LoginForm from './LoginForm'; it('should render login form', () => {

const component = render.create(<LoginForm />); expect(component.toJSON()).toMatchSnapshot();

});

Based on test description, you assume it will render the LoginForm component without crash, but you don’t know what the child components are. It’s even more frustrating when LoginForm is rendered for either login or sign up based on a boolean prop like this:

it('should render login form', () => {

const component = render.create(<LoginForm isNew={false}/>); expect(component.toJSON()).toMatchSnapshot();

}); it('should render signUp form', () => {

const component = render.create(<LoginForm isNew={true}/>); expect(component.toJSON()).toMatchSnapshot();

});

By just reading the tests, you can’t tell what is the difference between login form and sign up form. Now you have to switch to the code to see how it renders. You also want to switch to snapshot file to see what it renders if the test was not written by yourself. It takes you a few clicks, a few key strokes, and a few scrolls to reach where you want to see. Wouldn’t it be better if you can simply read everything in your tests?

No need to debug

Sounds good? (Sarcasm indeed…). After you change your code, run tests, some of the snapshot tests failed.

Failed snapshot test output

You assume it fails because of your intended change. You simply update snapshot by running jest --updateSnapshot or press u if you run test with --watch . Now all tests pass. You’re an expert in fixing failed snapshot tests!

When this becomes a habit, you probably don’t bother to take a careful look at the difference in console. What if your intended change also introduces unintended output?

I don’t know why tests failed. Update snapshot. I don’t know why tests passed.

Test is easy to break

After you change something, some tests should break. It makes perfect sense. However, it takes your attention away from something that really matters.

In general, you should write test for the result, not the implementation. You might assume snapshot testing only cares about what are being rendered, but it actually includes the implementation as you can see below:

exports[`App should render correctly`] = ` <div

className="App"

>

<form

className="form"

>

...

</form>

</div>

`;

className is considered as implementation because it’s just the naming that links to actual CSS. When you want to rename this kind of implementation, you don’t expect any of your tests to break, but it does.

No granularity

Snapshot testing is great for the big picture, but it doesn’t tell you exactly what went wrong. It could be your typo, your logic, or the child components.

Photo by Dmitry Ratushny on Unsplash

You might argue that you can have smaller tests for better granularity. If that’s the case, why do you even need snapshot testing in the first place? Multiple small tests is much better than a big snapshot test.

If not snapshot testing, what then?

Snapshot testing was introduced not long ago, so people were able to write good code and tests without it, so are you. Trust me.

There are other great testing libraries like enzyme or react-testing-library. The objective is to focus on things that matter to users.

If you’re testing a login form, you want to make sure the form renders an email field, a password field, and a login button. You don’t have to write tests against colour, font size, or spacing because you can see them when you run it.

Nothing good about snapshot testing at all?

After all, snapshot testing can be a good devil if you know what it does and use it with correct philosophy. Snapshot testing was specifically created for UI test. If you are concerning every single details being rendered in your component, snapshot testing is probably your guardian angel.

Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly. — https://jestjs.io/docs/en/snapshot-testing

You can further read this article to decide whether snapshot is good for your project.

Final Words

I don’t go fully against snapshot testing because it does what it is intended for. It becomes counter-productive when people abuse it or write it for the sake of having test coverage. If everyone in your team feels the same way, stop using snapshot testing once and for all. Instead, write more meaningful tests. Happy testing 😃!