Refactoring React Components with Jest’s Snapshot
Here come the jesters…
So what is Snapshot? Snapshot is a feature offered in the Jest library that allows developers to compare the current UI output of a particular component with a previously rendered UI output of the same component.
The official Jest docs describe the functionality of this feature in the following manner.
‘Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly… Instead of rendering the graphical UI, which would require building the entire app, you can use a test renderer to quickly generate a serializable value for your React tree.
In essence, this feature will take a virtual ‘snapshot’ of your component’s rendered UI in the form of a ‘serialized tree’ (example below) and it will keep this snapshot in storage so that it can be compared to future renderings of the same component. When running your tests, if something in the tree has changed, the Snapshot test for that component will fail. At that point you will have two options. Either adjust your code so that the output matches the snapshot or replace your current snapshot with a snapshot of your component in its current altered state.
If it ain’t broke, don’t fix it… refactor it!
So how can Snapshot be helpful with refactoring code? Good question. Imagine you’ve just finished writing an app. An incredible app. Maybe the best app ever written by anyone. Ever. This app has kept you up morning and night for the past 3 months and you can’t believe that it’s finally done. Not only is it done, but it is running with zero errors or bugs and on top of that it is visually stunning.
Just as you take a step back in order to give yourself a well deserved pat on the back you realize that despite the apps beautiful design and flawless functionality the code itself is essentially unreadable. You’ve put all of the apps functions, JSX and CSS into one single gargantuan of a React component…
You now realize that before submitting your code for review you’ll need to clean it up. The question is, how can you ensure that your refactoring will not alter your UI ? This is where Snapshot comes in to play as it will allow you to monitor your UI output as you make your changes. If something you change in your code changes the output your Snapshot test will fail and the altered portion(s) of the serialized tree will be highlighted in the test output for you.
Let’s walk through a simple example of how this might work.
First thing we want to do is to ensure that Jest is properly installed. (Install - Walkthrough)
Now, let’s take a look at our example’s current graphical UI output…
…The code that is rendering this output.
…The code for the test spec that is responsible for taking and testing the Snapshot (Check the docs for naming convention(s) for test files and folders).
…And the Snapshot itself (this code will be automatically generated by Jest on the initial run of the spec shown above test and placed in a dedicated folder named __snapshots__).
Now let’s say we wanted to refactor our code so that the header portion of the code is no longer in the same component as body of the code. This would result in two components.
A brand new component that we’ll call ‘Header’ that contains only the header portion from our original component…
As well as our original component that now imports and renders the newly created Header component in place of where our header JSX use to be…
When we run our tests, we see that our snapshot test is still passing despite the fact that the code itself has been altered ensuring us that our refactoring has not impacted our UI.
Let’s now change the code a bit so that it alters our serialized tree. Here is the main component once again, but this time we are referencing a difference source file for our ‘img’ tag. Instead of “crown1.png” we are going to set “crown2.png” as the image source.
If we now run the test now we get the following error message.
As you can see, we have failed our test. Our ‘received value does not match stored snapshot…’ The test returns to us the portion of our code that has been altered. The code highlighted in green displays the value of our stored snapshot and the code highlighted in red the new altered value.
Maybe this was intentional and we want to replace ‘crown1.png’ with crown2.png.’ If this is the case, we simply run ‘npm test — -u’ and it will replace our original Snapshot with a Snapshot of our current altered output.
Let’s now look at a different scenario. Imagine ‘crown2.png’ was sent to us by our graphics design team in order to test out a slightly different tone of gold in the crown image. After reviewing the graphical rendering of the page with this alternative image the design team asked that we move forward with the original version of the crown, ‘crown1.png.’ However, this was overlooked by our programmers and ‘crown2.png’ was not removed. Though visually different from one another, without someone looking closely at the image and checking the desired tone vs the actual tone of the crown referenced in our component we may not notice the difference and end up publishing our app with the wrong graphic. Luckily, Snapshot would catch this error and give us the chance to correct the mistake before publishing.
The Jest Snapshot feature is incredibly easy to set up and run. Don’t let it’s simplicity fool you though as it is a very powerful tool in assisting programmers ensure that their changes in code are not effecting their UI output. Once you feel that your UI output is where it needs to be, run the Snapshot test on that component and check any future changes / edits to against it to ensure that you are not altering the output.