Lesson 6 of 7 · 35 min

React Native Testing and CI: Unit, Component and End-to-End Tests

Set up a practical testing strategy and a CI pipeline that catches problems before they reach users.

In this lesson1. A testing strategy that fits mobile2. Component tests with Testing Library3. End-to-end tests4. Continuous integration

A testing strategy that fits mobile

Mobile bugs are expensive because fixes need a new store release and users may not update quickly. A layered testing strategy catches most problems early without slowing the team down. Write many fast unit tests for pure logic such as price calculations, date formatting and validation. Write component tests for important screens and interactions. Write a smaller number of end-to-end tests for the critical journeys that must never break, such as sign-in and checkout.

Focus testing effort where failure hurts most. For a pharmacy delivery app, the prescription upload and payment flow deserve more tests than the settings screen. For a banking app, anything touching balances or transfers comes first. Use a simple risk list per feature to decide. Aim for confidence rather than a coverage number, because high coverage of trivial code can hide gaps in the flows that matter.

Component tests with Testing Library

Jest is the standard test runner for React Native, and React Native Testing Library lets you test components the way users interact with them: finding elements by visible text, labels and roles, then pressing and typing. Tests written this way survive refactoring because they do not depend on internal component structure. Adding accessible labels to make tests work also improves the app for screen reader users, which is a useful side benefit.

Mock at the network boundary, not deep inside your components. Tools such as Mock Service Worker, or a simple mocked API client, let component tests exercise real hooks and data fetching logic with predictable responses. This catches bugs in how screens handle loading, errors and empty results. Keep test fixtures realistic, including long names, missing optional fields and non-English text such as Hindi or Arabic, which often reveal layout problems.

typescript · illustrative
// Illustrative test with Jest and React Native Testing Library.
// See callstack.github.io/react-native-testing-library for current APIs.
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react-native';
import { LoginScreen } from '../LoginScreen';

test('shows an error when the password is empty', async () => {
  render(<LoginScreen onSubmit={jest.fn()} />);

  fireEvent.changeText(screen.getByLabelText('Email'), 'asha@example.com');
  fireEvent.press(screen.getByRole('button', { name: 'Sign in' }));

  expect(await screen.findByText('Password is required')).toBeTruthy();
});

test('calls onSubmit with the entered values', () => {
  const onSubmit = jest.fn();
  render(<LoginScreen onSubmit={onSubmit} />);

  fireEvent.changeText(screen.getByLabelText('Email'), 'asha@example.com');
  fireEvent.changeText(screen.getByLabelText('Password'), 'correct horse');
  fireEvent.press(screen.getByRole('button', { name: 'Sign in' }));

  expect(onSubmit).toHaveBeenCalledWith({
    email: 'asha@example.com',
    password: 'correct horse',
  });
});

End-to-end tests

End-to-end tests run your real app on an emulator or device and drive it like a user. Tools such as Maestro and Detox are common choices in the React Native community; check each tool's documentation for setup with your project type. Keep these tests few and focused on critical journeys, because they are slower and more fragile than unit tests. Use stable test IDs or accessible labels, seed test data through a test backend and avoid fixed sleeps that make tests flaky.

Run end-to-end tests against a staging backend with predictable data. Reset that data before each run so tests do not depend on each other. When an end-to-end test fails, capture screenshots and logs automatically so developers can see what happened without rerunning it locally. A flaky test that everyone ignores is worse than no test, so fix or remove flaky tests quickly.

Continuous integration

Continuous integration runs your checks on every pull request, so problems are caught before merging. A good pipeline for a React Native app runs type checking, linting and unit and component tests on every change, and runs builds and end-to-end tests on the main branch or before releases. Popular options include GitHub Actions, GitLab CI, Bitrise and Expo's EAS Workflows. iOS builds need macOS runners, which affects cost and speed.

Make the pipeline fast enough that developers do not avoid it. Cache dependencies, run independent jobs in parallel and keep the pull request checks under a few minutes where possible. XDQ Labs projects block merging when type checks or tests fail, which keeps the main branch always releasable. Add a manual approval step before any job that submits to the stores, so releases remain a deliberate decision.

Treat CI configuration as code that deserves review. Keep workflow files in the repository, pin the versions of build tools and actions you rely on and store signing credentials as encrypted secrets in the CI provider, never in the repository. Run a scheduled build at least weekly even when nobody is merging, so you learn about broken dependencies or expired certificates before an urgent release depends on them.

  • Every pull request: type check, lint, unit and component tests
  • Main branch: build the app and run end-to-end smoke tests
  • Release: signed production builds and store submission with approval
Key takeaways
  • Use many unit tests, focused component tests and few end-to-end tests
  • Prioritise tests by the cost of failure, not by coverage numbers
  • Test components through text, labels and roles like a user would
  • Fix or remove flaky end-to-end tests quickly
  • Block merges on failing checks and gate store submissions with approval
Try it

Write two component tests for your sign-in screen using Testing Library, covering a validation error and a successful submit. Set up a CI job that runs type checking, linting and tests on every pull request. Add one end-to-end smoke test for your most important user journey.

Check your understanding

1. Which tests should you have the most of?

2. Why find elements by text, label or role in component tests?

3. What should happen before CI submits a build to the stores?

← PreviousReact Native Performance on Low-End Android DevicesNext lesson →React Native App Store and Play Store Release Checklist
XDQ xAgents · AI lab for your company
While you sleep, your AI agents keep working.

From USD 49 or INR 2,999 a month. A person approves every step.

Start free, 14 days →See every agent300 credits · no card needed · prices include taxes🚀 Startup? Free landing page for 30 days →Need custom software or a security review? Send a brief →