Introduction to Testing React Native Apps

, ,
Reading Time: 6 minutes

React Native is one of the most used frameworks for cross-platform mobile development. As the app grows, testing becomes crucial to keep the stability. It’s essential to ensure that the features we add don’t break other parts of the app.

Why should we test?

It’s hard to release a fix bugs on mobile apps later, due to app reviews taking a long time. For those who are frustrated with the review process, have an article with tips on how to avoid rejections: https://appcircle.io/blog/app-store-review-tips-how-to-get-along-well-with-apple-and-avoid-app-rejections/

Benefits of testing on mobile apps are:

  • Well-tested apps can easily add new features. You know early if some other part breaks.
  • Helps you to understand the purpose of your code. While writing tests, you also think what can go wrong and develop accordingly.
  • Maintainable code. Dividing your code into small units will create more maintainable code.
  • Get ready for dependencies breaking after an OS update.

Types of Tests

  • Unit Test
  • Integration
  • End to End(E2E)

We’ll cover all the test types in React Native development. Let’s start with a sample app.

Sample React Native App

You can find the source code located here: Appcircle’s React Native Sample App

1. Fork the repository

Go to https://github.com/appcircleio/appcircle-sample-react-native and hit Fork button to fork this repository to your Github account.

2. Connect it to Appcircle

The first step is getting started with Appcircle. Just go to https://appcircle.io/start and register.

3. Create a build profile by clicking Build, Build Profiles and then clicking “Add New”

Step 1: Add a Build Profile

4. Fill in the details of your build profile

Step 2: Add a Build Profile

5. Click your newly created build profile and connect your repository.

6. Give access to Appcircle and fetch your forked repository

Import Your Repository

7. You should see all the branches and commits in this repository.

Our repository is now connected to Appcircle.

Preparations

We will clone this repository to our machine and write our tests.

1. Clone the repository to your machine

git clone https://github.com/[YOUR_GITHUB_USERNAME]/appcircle-sample-react-native

2. Open your terminal and go to the cd to application directory and install the dependencies

cd appcircle-sample-react-native
npm i
npx pod-install ios

 

The above commands will download a bunch of dependencies and set up your project. This process will create both Android and iOS projects. In this blog post, we will focus on the iOS project.

3. Next, we will run our project

npx react-native run-ios

If everything works, you will see the following default React Native template

It is time to create our tests.

Catching Bugs Early On

Before writing tests, we can also prevent most of the bugs by using linter and TypeScript. This project uses ESLint. If you type

npm run lint

You will see that it doesn’t show any warnings or errors. If you’re using VS Code, you can install the ESLint extension to display and fix the problems.

Now we can write our tests.

Writing Tests

React Native comes with Jest and react-test-renderer. If you open your __tests__ folder you will see your test files. Default test is very basic and only checks whether the app is rendered correctly.

import 'react-native';
import React from 'react';
import App from '../App';

// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';

it('renders correctly', () => {
renderer.create(<App />);
});

Let’s run the tests to check. Issue the following command

npm test

Our test passed and we got the green checkmark!

We will create our first test and add it to our project. When you’re creating tests, you import the components you’re going to test and write test scenarios. Let’s test our first component.

Snapshot Testing

Snapshots tests ensure your components’ UI don’t change unexpectedly. These tests render a component, take a snapshot, and then compare it to the previous snapshot. For components that use dynamic data, we need to provide static models / data to make tests fail only if there is a change in UI.

Create a file __tests__/homescreen-test.js

import 'react-native';
import React from 'react';
import HomeScreen from '../components/homescreen';

import renderer from 'react-test-renderer';

it('renders correctly', () => {
const home = renderer.create(<HomeScreen />);
expect(home).toMatchSnapshot();
});

If we issue the following command in the project folder, we can see the result of our new tests.

npm test

This test is slightly different than the previous one. We also create and check the snapshot after we render the component. When the tests are finished, you will have a __snapshots__ folder. This folder will have human readable snapshots of the components in JSON format.

Next tests will use the snapshots from this folder and check if the component is modified. Let’s see how this works. Let’s change our component slightly and see what happens. Edit homescreen.js and add ! after the Appcircle.io and run the tests again.

The test is failed because our snapshot doesn’t match the new component. Use snapshot tests to ensure your UI doesn’t break after a change. Provide static data to prevent tests from failing due to data change.

Unit Testing

We can also test the functionality of our components by using Unit Tests. In our app, if we click the “Next” button, we see our details screen component. It has basic text input and a Login button. The login button is only enabled if the entered text is a valid email.

Since this test requires user input, we will use another library for our test, @testing-library/react-native

Create a file __tests__/detailsscreen-test.js

import 'react-native';
import React from 'react';
import DetailsScreen from '../components/detailsscreen';
import {fireEvent, render} from '@testing-library/react-native';

it('should only accept correct email', () => {
const {getByTestId, getByText} = render(<DetailsScreen />);
const input = getByTestId('email');
const button = getByText('Login');
// Login button starts as disabled
expect(button.props.disabled).toBe(true);
fireEvent.changeText(input, 'invalid email');
// Email is invalid, button is disabled
expect(button.props.disabled).toBe(true);
fireEvent.changeText(input, '[email protected]');
// Email is valid, button is enabled
expect(button.props.disabled).toBe(false);
});

First, we render our component. Then we find our email input and login button with their testID and their text content. Using testID is a better way to find your elements. Since text content may change frequently. After we find our components, we check the state of our button after different events.

if we run our tests again

npm test

As the app grows, there would be more tests and it would be hard to test every commit on our local machines. Appcircle can ease this process.

Automate Tests

Wouldn’t be nice to do these tests automatically for every commit?

Linting and Testing Our Code

Let’s add a new workflow just to test our code. Head over to workflows and a new Workflow and name it to anything you want. Since we’re just testing our code, we don’t need to build apk or sign it, we can remove some steps from the default workflow.

We will add two steps after the npm install step.

1. Linter

2. Testing

The final workflow will look like below

We can check our workflow by clicking the “Build” button and then selecting “Test Workflow”

We will have nice green checkmarks after the build process.

This is a manual way of testing our code. We can automate this by using Triggers. For more information about triggers, check our docs:
https://docs.appcircle.io/build/build-manually-or-with-triggers

Open triggers and change it like below and hit Save.

From now on, anything you push to dev branch will be tested automatically.

You can have multiple triggers for different scenarios. Triggers are very flexible can run different workflows one after the other.

Where to go from here?

To learn more and dive deeper into testing in React Native, check out the official docs of the projects mentioned here:

https://reactnative.dev/docs/testing-overview

https://jestjs.io/docs/tutorial-react-native

https://callstack.github.io/react-native-testing-library/


Share this post!