Unit testing in React Native with Jest and Enzyme

Unit testing is the first line of defence for your codebase and is key to a healthy and maintainable app.

If you don’t like testing your product, most likely your customers won’t like to test it either. — Grasshopper

There is a reason why unit tests makes the base of the Testing Triangle. Idea here is, more tests needs to be written at the bottom of the triangle and less and less tests when we move up on the triangle. So more unit tests than integration test etc etc.

Famous Testing triangle

What are we unit testing exactly?

When unit testing, we are checking the smallest unit in our codebase to find out how happy, unhappy and edge cases work for that piece of unit. For example, a unit could be individual methods and functions in classes or really any small pieces of functionality. We mock out dependencies in these tests so that we can test individual methods and functions in isolation.

What frameworks do we use?

These test are written using testing frameworks and for this article we will be using Jest, Facebooks javascript testing framework together with Enzyme, Airbnb’s testing utility for React.

Configure Jest and Enzyme

Let’s start from scratch and create a new react-native app. Refer to Getting Started — React Native for setup instructions.

Jest comes out of the box so let install Enzyme. Refer to Enzyme install instructions for further info

Install enzyme dev dependancies by opening up a terminal session in project root directory and executing the following command in it. I’ll refer to this folder as rootDir from here onwards.

npm i — save-dev enzyme enzyme-adapter-react-16 react-dom

Create a jest.config.json file in rootDir.

Refer to Jest configuration guide for more info on the available options

Create a folder called jest inside rootDir and then create setup.js file in rootDir/jest/ folder.

Now we are all set to start unit testing in React Native with Jest and Enzyme.🎉 😎 🎊

Taking a snapshot in time 📸

What is snapshot testing?

With Jest we have a very easy way to test if a component is rendered correctly given certain props and state. It’s called snapshot testing.

A good snapshot stops a moment from running away — Eudora Welty

Snapshot tests are a very useful tool whenever you want to make sure your UI does not change unexpectedly.

A typical snapshot test case for a mobile app renders a UI component, takes a screenshot, then compares it to a reference image stored alongside the test. The test will fail if the two images do not match: either the change is unexpected, or the screenshot needs to be updated to the new version of the UI component.

Basics of snapshot testing

Lets setup a basic button component so that we try out snapshot testing it.

Create a Button.js file under rootDir/src folder.

Button will look like this when rendered

Now let’s create the test (sometimes called spec) file. Create Button.test.js

Alternatively you can call this file Button.spec.js as well. Jest will accept both extensions and will append file extension as necessary for auto generated files such as snapshots.

In this case we have used Enzyme’s shallow rendering to help us create a snapshot.

Running tests

It’s time to execute your test. We do this by invoking the following.

npm run test

Since this is the first time we run this snapshot test, Jest will create a snapshot file for us inside the folder __snapshots__

Folder structure

What happens when something changes?

The usefulness of snapshots come into play when you (or someone else) changes a presentational aspect of this component. As an example try changing fontSize to something other than 16 in Button.js and run tests again. You will see an error like the following.

Received value does not match stored snapshot

Which means, we have changed the way how this component gets rendered. If this is intentional, we can update the snapshot by passing in the flag -u to npm run test like so

npm run test -- -u

If the change is not intentional, that means we have done a change unintentionally that will change the way this component gets rendered. So we will have to go ahead and revert our change.

I’ve changed the Button.js to be

  1. Class based component
  2. Custom function is invoked when onPress is tapped
  3. Change color of button depending on ‘primary’ prop

Lets test this puppy by running the tests and updating snapshots (thats npm run test -- -u if you forgot)

Coverage report

When we run tests, jest generates a nice coverage report for us. To have a look at it, navigate to rootDir/coverage/lcov-report/index.html.

Clicking on Button.js will reveal the coverage visually. From this we can deduce what statements/branches not covered.

On line 24, branch not covered comes up because we are setting default value for primary props to be true in Button component. And in Button.test.js we don’t pass in a prop for primary so the default value gets used.

const component = shallow(<Button label="test label"/>)

Change Button.test.js to remedy this

Now all that is left is to test onPressHandler function. But for us to do that, we need to talk about mocking 😮

Don’t mock me 🐒

The idea behind mocking is to replace something we don’t control with something we do.

Mock functions are also known as “spies”, because they let you spy on the behaviour of a function that is called indirectly by some other code, rather than just testing the output.

Lets focus on two ways Jest helps us out with mocking

  • jest.fn() — mock function
  • jest.mock() — mock module

Mocking functions with jest.fn()

jest.fn() provides ways to capture calls, set return values and change implementation of a function. Refer to Jest mock function api for further information.

Let’s user jest.fn() to test if onPress is getting called inside onPressHandler()

  1. Mocking function with instance of a jest function.
  2. Passing in mock function as a prop (as onPress). Upon onPress we expect the mock function to get called.
  3. Manually calling onPressHandler()
  4. Checking if mock function gets called and how many times

Mocking modules with jest.mock()

jest.mock() is the module mocking solution from Jest.

Let’s use it to see if Linking.openURL() is getting called or not and in what circumstances.

  1. Set openURL module function to jest.fn
  2. Preparing a reusable shallow rendered instance so that we don’t repeat ourselves trying to create the same shallow instance with the same props
  3. mockOpenURL should be called since we have passed in ‘url’ prop
  4. mockOpenURL should NOT be called as we have NOT passed in ‘url’ prop

Anatomy of a unit test

There are few different ways to structure your unit test. The most common pattern being AAA (Arrange Act Assert).

Idea is to seperate your test into the said three sections so that it’s easier to reason about the test.

Some good practices 🏃

It’s important to write testable code in the first place.

Unit testing should begins before we actually write any tests. Writing testable code is as (if not more) important as writing unit tests themselves.

Only unit test one piece of code / function at a time.

Don’t try to unit test a scenario where you call an api and then it resolves and set a state and based off on that state the component renders something different.

Rather, unit test those different units on their own. Check function that calls the api and test upon resolve/reject what state it sets. Then in another test check given this state how does a component render.

Given a certain input, whats the output?

Thinking in these terms will make reasoning about what and how to unit test much easier.

AAA (Arrange, Act, Assert) pattern

It also helps to think in terms of AAA (Arrange, Act, Assert) pattern when you are starting off writing test as it gives a clear separation of concert for the structure of your tests.

Wrap up

These are few basic ways you can start unit testing your React Native codebase with Jest and Enzyme. They both have great documentation so there is no reason you should not unit test your codebase anymore. ⭐️ 🌟 ✨

Thank you for talking time to read this story!. If you enjoyed it and/or found it useful please leave a 👏. If not, please leave a comment and let me know what I can improve for the next one. Happy unit testing.

Photo by Victor Rodriguez on Unsplash

Edit 14 Dec 2018 : Rectified an issue in the Configure Jest and Enzyme section.

Shanaka 'Sha' Senevirathne

Written by

Software Engineer. Sydney, Australia. http://www.shanaka.info

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade