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.
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.
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
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.
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.
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__
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
- Class based component
- Custom function is invoked when onPress is tapped
- 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)
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.
jest.fn() to test if
onPress is getting called inside
- Mocking function with instance of a jest function.
- Passing in mock function as a prop (as onPress). Upon onPress we expect the mock function to get called.
- Manually calling onPressHandler()
- 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.
- Set openURL module function to jest.fn
- Preparing a reusable shallow rendered instance so that we don’t repeat ourselves trying to create the same shallow instance with the same props
- mockOpenURL should be called since we have passed in ‘url’ prop
- 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.
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.
Edit 14 Dec 2018 : Rectified an issue in the Configure Jest and Enzyme section.