My take on Snapshot Testing
Tags: testing
Published: 2022-07-19 16:10:03

It's true that [Snapshot Testing](https://jestjs.io/docs/snapshot-testing) got a lot of traction in recent years in the frontend world due to the apparent advantages like
1. Avoiding writing a lot of assertions for complex objects
2. Avoiding writing sometimes hard-to-read assertions by mixing a lot of `objectContaining`/`arrayContaining` etc.

But in my opinion, **it spread too much compared to the actual value** it brings because if used without discipline, it just moves the burden of writing the assertions from the writer to the reader and the refactorer, leaving a lot of ambiguity to the latter. Let's see why.

## Vague assertions bring vague clarity and loss of knowledge

Consider that the code is written once, refactored tens of times, and read hundreds of times.

Said that when you are refactoring some code, you need to extract the functionalities and the tests play a relevant role in documenting what the code does. Then you encounter a `toMatchSnapshot`, and you must decide if
1. Going through the snapshot, trying to understand it. Usually, it's not easy because they include big objects
2. Skipping the snapshot and hoping you respect it during the refactor

In both cases, **the refactorer cannot understand the gist of what the code does**, and they must treat every change as a regression. But is it crucial that nothing changes? What the code does internally that must guarantee the returned object looks the same? Due to the missing context, to understand it, the refactorer must go through the code, through the consumers of the code, etc. to get a complete picture of how the code is used and for what. Or better, the refactorer must do this process way **more carefully** compared to the case where a test says "I care about X, Y, and Z".

The "I don't want to write so much assertions" cost the original writer (that has the context in mind that brought to write the original code) does not pay will be paid by the refactorer (that misses the original context so it's harder for them do refactor the code).

## Vague assertions bring vague acceptance of the changes

When something changes in the code and the snapshot differs, the refactorer has to check what's changed carefully. In my experience, especially when you deal with big React components, the changes are accepted without a proper understanding of them, just because the developer is tired of continuously receiving a "this is changed, do you accept it?" feedback. Hence, it's like not having the snapshot testing at all.

## Inline snapshots vs external snapshots

Inline snapshots usually reduce the problem because the snapshot is directly put into the test code, but the above considerations are still valid. External snapshots are a problem because if a test asserts multiple snapshots or more tests in the same file use snapshot testing, the file containing the snapshots gets unreadable in a while.

## Types remove the need for snapshot testing

I'm not a fan of using TypeScript in the tests (or, I mean, using it in the same way I use it in the source code) because it can get the test code less readable. But using TypeScript can at least allow validating the big object used in the tests, resulting in having typed big objects defined upfront instead of snapshots saved after the first run of the test.

## Dynamic data

Everything that changes over time (ex. dates, as reported in the first example) cannot skip to the writer of the code, usually resulting in test failures after a while. This is not a pleasant experience

## Snapshot testing use cases

So, what are the use cases for snapshot testing in my opinion?

1. Testing that the object does **not contain more properties than the expected ones**. If I test that an object contains X, Y, and Z, a snapshot allows ensure that the object does not contain anything more than X, Y, and Z. (this is a great [beaussan](https://twitter.com/beaussan)'s idea). Note: I'd add a document in the test explaining the purpose of the snapshot test

2. Testing that **something cannot change at all**! Every change in the object will be a red alert, and no changes will be accepted at all! I can remind when I migrated an HTML newsletter generator and nothing, **nothing**, could change in the resulting generated HTML (if you have ever coded responsive newsletters, you get me).

3. **Testing the unknown** or integrations. When you refactor some code, chances are your code interacts with other pieces you do not know or understand. In this case, **every single change** must be considered a failure that requires a long investigation time that must stop whatever we are doing.

Apart from that, I seriously challenge every use of snapshot testing in the codebase because usually, it's a smell.

If you have other use cases you think it's worth sharing, please leave a comment and let's discuss them 😊 also, if you disagree with me, please let me know 😊