One of the most common ways to write test tests in React is through snapshot tests. They are very convenient, easy to use, and require little to no testing effort. But over the past years, they have fallen out of favour more and more in the field of React. Most of the people in the community have concluded that snapshot tests barely hold any meaningful value. Whereas behaviour tests that are reflective of how users interact with your application are more reliable, realistic, and useful.
Due to this, most React developers have gotten rid of snapshot tests entirely and migrated their testing codebase towards behaviour tests focused style. Nowadays, snapshot testing is often viewed as a fake form of testing, only providing a false sense of security. You’ll often hear that unless you have specific reasons to perform snapshot testing, it’s recommended to stay away from them.
After hearing that piece of advice over and over again, I knew that I shouldn’t use snapshot testing as a default and that it only really had specific use cases. But I didn’t understand when I should use it. What are use cases for snapshot testing? When do they make sense? What are they actually useful for?
Over the past months, I’ve looked out for these use cases while developing React components. I wanted to see in which scenarios snapshot tests would make more sense than standard unit or behaviour tests. After spending quite some time on it, I finally understood the underlying issue with snapshot tests. With this, I realised why snapshot tests were abandoned and what makes them so meaningless, but also in which use cases they are most relevant. This article will that knowledge with you.
One of the reasons that snapshot tests are nowadays often frowned upon is that they are very volatile. The smallest change in the resulting DOM structure of a React component will result in a failing test. More often than not, these are false-negative test results that provide little to no value and are categorised as “noise” in the codebase.
Snapshot tests are so volatile because they capture the entire DOM structure of a React components tree. Nothing more, nothing less. In most use cases, developers don’t need the entire DOM structure in their tests. But it’s still part of their verification process in their tests. The result is that those verifications target way too much code, while only needing a small part of it. This leads to the common frustration that snapshot tests fail often and fail on irrelevant parts.
So the root problem of this frustration is how much code we use compared to what we need. If only 5% of the used code is relevant to the test, then it’s not weird that the test will fail on irrelevant parts. Especially when the code that is used is very subject to changes.
The solution to prevent these false-negative test results is to make our tests be more specific and only use what’s necessary for the verifications process. In most cases, this means staying away from snapshot testing.
For most developers, the problem with snapshot tests was their volatility. The underlying reason for this volatility was that a lot of code was retrieved for the verification process, but only a very small fraction was relevant. With this knowledge, it’s easier to understand the scenarios in which snapshot tests are most relevant.
It means that snapshot tests are most useful in scenarios where a lot of the retrieved code is relevant to the test. For snapshot tests, that code is the resulting DOM structure. So, implementing snapshot tests is most relevant in tests cases that target a majority of a React component’s DOM structure.
Okay, but what are scenarios where you want to perform verifications against a majority of a React component’s DOM structure? To be honest with you, there aren’t a lot of them. In three years of professional React development, I have rarely encountered reasons that actively justify snapshot testing. But the ones that I did encounter were:
- Verifying the DOM structure for different purposes. 🔍 The main reason to use snapshot testing is when you need to verify the resulting DOM structure of your React component(s). Most of the time, this process involves checking for HTML attributes, classes, or the presence of certain HTML elements. Doing this can have a lot of different purposes, including but not limited to verifying SEO, animations, accessibility, and the structure itself.
- Preventing regression in the DOM structure. 🐛 Related to the previous topics, there will be scenarios in which you want to preserve the same DOM structure between different versions of your React component(s). In my current team, we are mainly responsible for developing widgets for the platform. One important criteria is that we don’t make breaking changes to the DOM structure of those widgets between releases. For this, the most optimal solution for us is snapshot testing.
- Validating visual mounting or unmounting through CSS. ⚙️ Under normal circumstances, mounting or unmounting React components is done through respectively adding or removing them from the React render tree through JS. But in certain scenarios, this will lead to undesired behaviour. A common solution is to not go through these lifecycles, but only provide feedback to the user visually through CSS. This means that the components are still part of the DOM structure, thus being a prime candidate for validation through snapshot testing.
All of these scenarios are not only proper use cases for snapshot tests. Implementing these use cases is also most ideally done with snapshot testing due to their reliance on the resulting DOM structure. Using other forms of testing will only make it more complex, difficult, and verbose. So if these are your use cases, snapshot testing is not only a proper choice but also recommended.
Over the past years, snapshot testing has shifted out of favour in the React community. Due to its volatility and consistent false-negative test results, it has also received a relatively bad name. This leads to a migration away from snapshot testing and adopting more behaviour focused testing in React codebases. Nowadays, you’ll often be recommended against snapshot testing as a default and only use it in specific scenarios. But what are those specific use cases?
After spending quite some time on this issue, the underlying issue was that only a very small fraction of the code was relevant for the test verifications. In snapshot tests, the entire React DOM structure of a React components tree is retrieved, but the value of our tests only depend on small sections of it. So a lot of the DOM structure is left irrelevant, which causes volatility and false negatives.
This means that snapshot tests are most useful when as much of the retrieved DOM structure is used in the test case. Based on this information and personal experience, proper use cases for testing React components would be verifying the DOM structure for different purposes and preventing regression in the DOM structure between versions.
Snapshot testing shouldn’t be used as a default way of testing and is only recommended in certain specific use cases. Although those scenarios are very rare to come across, they do exist. In those cases, snapshot testing is more often than not only a proper use case but also the most ideal solution.