Build Youtube in React 06: Snapshot Testing using Jest and Enzyme
1. Testing React components in Jest and Enzyme
Just one more thing before we go on. We have already quite a few components and as part of our a professional software development approach, we would also like to test them as well.
Right now, our components are basically just markup and don’t contain any logic. But we can still test them!
1.1. Snapshot Testing
Creating a snapshot
can be compared with taking a photo in the real world. When you create a snapshot
, you basically create a photo of the component, i.e. you somehow store, how the component is supposed to look. Now if the component is changed later on, the photo (the snapshot
) will look differently than before. When you run the available snapshot tests, you can compare the last stored snapshot
with the current component. Therefore, it is immediately apparent what has changed. This approach is way easier and faster to do than other types of frontend testing we can do. These snapshot tests
are unit tests. They don’t test how the different components interact, but are focused on one single component.
Fortunately there is a variety of tools out there that make our snapshot testing
life much easier. One of the most popular snapshot
testing packages is Enzyme which is developed and maintained by AirBnB. Enzyme itself is responsible for creating and storing component snapshots
. However, to set up a test bench, we need more functionality than creating snapshots
. We need a package that allows us to assertions and can compare different snapshots
. Therefore, Enzyme is often used together with Jest, one of the most popular React/Javascript testing frameworks
out there.
We’re also going to use them together.
1.2 Setting up Jest and Enzyme in create-react-app
First, we need to install Jest and Enzyme via npm
or yarn.
The nice thing about create-react-app
is that Jest is already pre-installed. So we only need Enzyme.
So the thing is that Enzyme
for itself is not sufficient. Depending on which React
version you use, the overall render process is different. React 15
renders component differently than React 16
and so on. Since we want to use one universal API for Enzyme, we need an adapter that connects Enzyme to the React
version we are using. As we are using React 16.x
we install the enzyme-adapter-react-16 component.
Why do we also need the react-test-renderer package? The thing is that React
typically renders your component to the DOM
. However, when we are testing, we don’t want to render to a DOM
, we just want to render to Javascript objects because then it is easy to create snapshots
.
Finally, we also install the enzyme-to-json package. Jest and Enzyme are basically two separate libraries, so per default Jest
does not understand Enzyme objects. Therefore, we need a package so that we Enyzme wrappers are compatible with Jest.
1.3. So why again do we need this adapter?
I’ll give an analogy on why Enzyme needs a React
adapter. Maybe that’s easier to understand.
Suppose you want to connect some periphery to your computer. Your machine only has USB-C ports. So if you want to a device with USB-A, you need a USB-C to USB-A adapter. If you want to connect to a monitor, you need a USB-C to HDMI / display port cable. So by having USB-C only, you have a universal physical interface that you can use to connect other devices to. So no matter what physical interface your periphery has, you can always connect to it as long as you have an adapter.
In this analogy, USB-C is the Enzyme API you use to write your testing code. We don’t want this API to change because then the Enzyme API would differ depending on which React
version we use. Continuing with the analogy, the different peripheral devices that have USB-A / Display port /HDMI interfaces are the different React
versions that you can write your tests against.
1.4 Configure Testing in create-react-app
Wow that was a lot of explanation. But don’t worry – since we have all the packages above, setting up testing in our create-react-app
based application is really easy. create-react-app
has documentation on how to setup up testing. Its test setup allows us to create a file in src/setupTests
where we can configure our testing.
- Create a new file in
src
and name itsetupTests.js
. The naming here is important.
Paste the following code into this file to configure your test setup:
What do we do here? Remember that enzyme-to-json provides a way so that Jest can understand Enzyme wrappers. We basically tell expect (an assertion tool that is part of Jest): look, we have a little bit of additional logic here so in case you encounter an Enzyme wrapper and you don’t know what to do with it, we give you an additional serializer so that it works anyway.
As already mentioned, we also have to configure Enzyme to our current React
version. In our case this is React 16
and we just use and adapter we imported from enzyme-adapter-react-16.
1.5. Testing our setup
Let’s see if our setup works.
Head over to your console and run
If you encounter an error like this:
you have probably mixed npm
with yarn
commands. Just delete your node_modules
and your yarn.lock
file and reinstall them again using
Apart from that everything should just work out fine and you a file watcher should have been started.
1.6. Writing tests in create-react-app
The create-react-app docs clearly list how our tests should be organized so that our test runner executes them:
.js
suffix in__tests__
folders..test.js
suffix..spec.js
suffix.
We are going to colocate our snapshot tests
with our components because they are unit tests.
We will also create __tests__
directories for each component because otherwise our logic is mixed with tests. I typically give unit tests a .unit.test.js
suffix because then it is immediately apparent whether it is a unit or an integration test.
Let’s test the components we already have.
2 Testing our HeaderNav component
- Create a new directory in
src/containers/HeaderNav
and name it__tests__
(the directory’s name is important) - Create a new file called
HeaderNav.unit.test.js
inside the directory you just created.
Let’s create our first snapshot test
:
Head over to your console and run
Notice that a folder called __snapshots__
within our __tests__
directory has been created that contains .snap
files. .snap
files contain the markup, the render
function returns from our components. Let’s illustrate this really quick.
2.1. Shallow Rendering in Jest and Enzyme
Let’s just talk about shallow rendering
real quick. Shallow rendering
means that we are executing the render method of a component, but don’t step down the entire view hierarchy. We’re just getting the markup that the render method returns and write this to a snapshot
file. Here’s an example that is really easy:
The ParentComponent.unit.test.snap
file above shows content of the snapshot
file, our test produces. And here you can see the difference between mounting and shallow rendering. Shallow rendering will not execute the render
method of custom components that are used within the current component. This is actually really good because our test for the ParentComponent
should only cover the ParentComponent
and not the elements it makes use of in its render
method. Like so, we are only testing one component. This is ok because each component will have its own snapshot test
, so nothing remains untested.
3 Snapshot test all the components we currently have in our project!
Now that we have understood the basic concepts of snapshot testing
, let’s apply this knowledge to our existing components.
Go ahead an create snapshot tests
similar to the one we have created in section 2 for
SideNav
Footer
SideBarItem
SideNavHeader
Subscriptions
Subscription
For brevity, I won’t show all the code here because it would be very repetitive. Try to do this on your own. You can also head over to this project’s git repository if you need help.
Just one more thing. Some components take different sets of props. E.g. SideBarItem
takes highlighted
, icon
and label
as props. You can snapshot
the component with different props like so.
You can also do that for the SideBarHeader
and the Subscription
component:
In case you were wondering about the describe
function: we use it to group tests for one component or one functionality together. It’s just best-practise.
4 Wrap Up
I know, testing is probably not your favourite activity. I get it.
But you can have greater confidence in the stuff you actually build. That’s why we do it in the first place.
As always, you can find this project’s code in our Github repo.
Follow @productioncoderPlease follow me on Twitter @productioncoder to stay up-to-date. I’m happy to receive feedback of any kind.
If you are interested in more high-quality production-ready content, please enter your email below.
Recent Comments