Unit test, or functional tests? This is the endless question you always asked yourself.

You may prefer unit tests because it is fast, quick to set up, and deterministic, but you wonder how to unit test your React components ?

Have you tried Jest snapshots ?

1) Set up a basic application:

You can use the wonderful create-react-app (the official React generator by Facebook) to set up a simple working React application.

npm install -g create-react-app create-react-app snapshots cd snapshots npm start

Then, you can crawl the url localhost:3000 and see a simple application. This application already has a basic unit test with Jest. Running npm test will launch Jest watcher.

If you want to create a react native project, you can't use create-react-app, however, a similar generator exists, you can check the official doc here.

2) Create a new component

As we already have unit tests set up, we are going to test our first component right now.

Damn it, we don't have any component yet! Ok, let's split our application into components. Create a folder src/components , and a file inside this folder : src/components/Intro.js . Now, paste the following code into the file we just created.

import React , { Component } from ' react ' ; import ' ../App.css ' ; class Intro extends Component { render () { return ( < p className = " App-intro " > To get started, edit < code >src/App.js</ code > and save to reload. </ p > ); } } export default Intro ;

Now, open App.js and replace the corresponding code with the use of the newly created component:

< Intro />

Don't forget to import the component at the beginning of the file:

import Intro from ' ./components/Intro.js ' ;

Your application should look the same as before.

3) Test the component

Now, we can create a new test file : src/components/Intro.test.js . For every file ending with .test.js, Jest will consider this file as a test file. Snapshot test are able to see if a variable (string, array or object) stays unchanged.

For instance expect({foo: "bar"}).toMatchSnapshot(); will generate a file: the "snapshot" of the object {foo: "bar"} on the first use. If you change the object later to {foo: "not bar"} , the test will fail and ask you to update the snapshot or to fix your code. Now, create your first snapshot test with a simple DOM element, a div.

describe ( ' Our first snapshot test ' , () => { it ( ' Should compare the component with a snapshot ' , () => { const component = ' <div>Hello Jest</div> ' expect ( component ) . toMatchSnapshot (); }) });

Look at your test console, the test is now successful.

Fair enough, now, let's test our component. To do this, we will need the package react-test-renderer , this will serialize your react component into a string. A similar renderer exists for React Native.

npm i --save-dev react-test-renderer

Now, import it in our test, we can also import our component src/components/Intro.js and React in our test file.

import React, { Component } from ' react ' ; const ReactTestRenderer = require( ' react-test-renderer ' ) ; import Intro from ' ./Intro.js ' ;

It is now time to test our component, replace your test with :

const component = ReactTestRenderer . create ( < Intro /> ); const json = component . toJSON (); expect ( json ) . toMatchSnapshot ();

Your test will now fail because the snapshot you generated before and the component you create in your test is not a div anymore, but is now a render of a React Intro component.



To update the snapshot, run type u if you are still in watch mode, or simply delete the snapshot file under the __snapshots__ folder. The snapshot has been recreated and matches the test, the test passes.

4) What now ?

From now on, your component is snapshoted, commit it with your code.

Let's try to modify our component src/component/Intro.js replace edit with modify .

The test fails and, as earlier, you can either change your code because the component comportment should not have changed or accept these changes by regenerating the snapshot.

5) Why doing these tests ?

Component snapshot tests are unit tests, so they are really fast, but they test your component behaviour. It allows you to check if your components appearence changes and the act as functionnal test. They are easier to maintain up-to-date that functional one but you can still interact with the component and see that the view update.