When developers discuss using Jest Snapshots in tests, they often form 2 groups. The ones, who like to use them extensively even in place of regular assertions, and the ones who hate snapshots and prefer not to use them at all.
In this dispute, I prefer the Middle Way as Snapshots and manual assertions can coexist. We only need to know what jobs are they created, and use the right tools.
- When Snapshots shine?
- When Snapshots gloom?
- Bad usages of Snapshots
- Good usages of snapshots
- Final words
When Snapshots shine?
Let's start with getting familiar with the strengths of using Snapshots in our tests.
Can assert big results
With regular assertions, we cannot assert the whole markup of React component, the whole state returned by Redux reducer, or some big chunks of data. Creating manual assertions for such cases would be either problematic or unpractical.
Are blazing fast to write
It’s a matter of dropping.
toMatchSnapshot() and running the test for the first time. When snapshot gets outdated you can replace it with a fresh copy by running the test with
-u flag. There is no way to create or update manual assertions covering such wide ground in comparable time.
When Snapshots gloom?
After getting familiar with their pros it’s easy to think about cases when Snapshots might cause more pain than gain.
After combining exact assertion with a wide scope of the test, then it’s no surprise that snapshots are brittle. Many developers are annoyed when after making some changes in the source code, the test in a seemingly unrelated piece of application turns red.
Too easy to update
Often developers who do not like snapshots say, that when the test breaks it just gets updated which can lead to expecting the incorrect state. With time, snapshots might degrade and not provide any value, even though they are cheap to add, developers maintaining such incorrect tests will pay the price of wasted time when they finally try to figure out what given test should really assert.
No information about what is being tested
Contrary to manual expectations, snapshots do not provide any information on what they are exactly testing. Just the end result and test title. which might be sometimes helpful.
Give a false sense of security/coverage
It might be the case, that developers feel they have the application tested, and have good coverage, while they use only snapshot tests. Because of all the characteristics mentioned before, snapshots provide little value when used as a main “testing force”. More than often developers will be confused why given snapshot failed and does it require an update of the fix of the code.
Not suitable for TDD?
Because Snapshots can only assert existing behavior, they cannot be used when following TDD. However, does this prevent adding snapshot after finishing the implementation? I don’t think so. With them, TDD workflow could look like this: Red, Green, Refactor, Snapshot.
Bad usages of Snapshots
Having tests with only snapshot assertion.
One of the cases, when snapshots provide more trouble than value, is when they are the only assertion in the test. Later on, when the test breaks the developers have little to none information what actually is tested, and should the snapshot be updated or there is really a bug in the code. Only good test description could sometimes help, but I wouldn’t rely on that.
Snapshot tests without descriptive names make more confusion than value
Using snapshots as a replacement for other assertions
The second anti-pattern is when hyped developers replace other assertions with just the snapshot. This is often connected to the issue I mentioned above, so instead of a few assertions, there is one snapshot. It might be an effect of incorrect belief, that snapshots are interchangeable with manual assertions, with the difference, that snapshots are faster to write.
Snapshots should not replace manual assertions
Mindlessly updating snapshots whenever they fail
The previous misuse of snapshots lead to another one, which is updating them whenever they fail, without checking what was causing the changed result. That’s because it’s hard to know what given test should really check if you only have a snapshot. In such reality not many will spend time and effort to understand what’s going on. It’s easier to just update and move on. However this can also be the case for manual tests, as someone might update/comment out/remove it only to have a green suite. So this is a human factor, not necessarily the fault of Snapshots.
Restricting snapshots scope (this one might be controversial)
Some say that big snapshots are a bad thing, as they cannot be easily reviewed. I don’t agree with this one, as restricting the snapshots scope is rendering them less useful in cases I see them work the best. I will explain why, in the next section.
Good usages of snapshots
Should be used together with regular expectations
After going through the up and downsides of snapshots it gets clear, that snapshots are perfect for catching regressions in unexpected places.
In that case, we should manually assert the results we care about in given test, and then drop a snapshot if we want to watch for unexpected changes. That way we’ll know when the “core” of our test broke, or we have some change in the end result somewhere around. Without Snapshot, we might not catch such changes as manually asserting everything is unfeasible.
Snapshots can be big
Bigger snapshots cover more ground. This is a good thing when we treat them as alerts that something has changed in a given area. So whenever they fail it’s just a matter of quick judgement. “My changes affect this place. Is it ok?” If the answer is “no” then snapshot probably saved us from introducing a regression.
Use (custom) serializers
Even though snapshots can be big, we should strive to have them as clean and meaningful as possible. This can be achieved with snapshot serializers.
When snapshotting React components rendered by enzyme.js it’s good to use enzyme-to-json to remove enzyme-specific data, and save only component markup.
In cases when we want to compare only the differences between the two states, we can use snapshot-diff.
Snapshot with and without serializer
As with all tools, we shouldn’t decide about using them or not, based on our emotions, or bad past experiences. We should rather understand the characteristics of a given tool.
So we have to be mindful about Snapshot testing features: the ability to assert big results, speed of creation and update, brittleness, no explicit information what is being tested, no use as primary assertion when doing TDD.
All that makes Snapshots a perfect support tool in addition to manual assertions whenever we want to check for regressions/unexpected changes in big structures like React Components, Redux Reducers etc.
And if you need help with your development or qualified engineers to augment your team - contact us!