Skip to main content
Unit Testing

Module Overview

Estimated Time: 4 hours | Difficulty: Intermediate | Prerequisites: Core React Native concepts
Testing mobile apps is harder than testing web apps. You are dealing with native module mocks, platform-specific behavior, asynchronous gesture handlers, and components that depend on device APIs. Many teams skip testing entirely because the setup feels daunting — and then pay for it with regression bugs that only surface after an App Store release (where a hotfix takes days, not minutes). This module covers unit testing with Jest and React Native Testing Library (RNTL), including component testing, hook testing, and mocking native modules. The goal is to give you a testing foundation that catches real bugs without becoming a maintenance burden. What You’ll Learn:
  • Jest configuration for React Native
  • Testing components with RNTL
  • Mocking native modules
  • Testing hooks and context
  • Snapshot testing
  • Code coverage

Jest Setup

Default Configuration

React Native and Expo projects come with Jest pre-configured:

Custom Jest Configuration

The transformIgnorePatterns setting below is the single most confusing part of React Native testing setup. By default, Jest does not transform code inside node_modules. But React Native packages ship untranspiled ES modules, so you must explicitly whitelist them. If you see “unexpected token import” errors in your tests, this regex is where to look.

Setup File


React Native Testing Library

React Native Testing Library (RNTL) follows the guiding principle: test the way your users interact with your app, not the implementation details. Instead of checking whether setState was called or a specific internal state variable changed, you query components by their visible text, accessibility labels, or roles — the same things a real user would see and interact with. This philosophy means your tests survive refactors. Rewrite a component’s internal logic from useState to useReducer? Your tests still pass because the user-facing behavior did not change. Install RNTL:

Basic Component Testing

Testing with User Events


Testing Hooks

Custom hooks contain reusable logic, and testing them in isolation (without a wrapping component) ensures they work correctly regardless of which component consumes them. renderHook from RNTL gives you a lightweight way to exercise a hook’s API and verify its outputs.

Custom Hook Testing

Testing Async Hooks


Testing Context


Mocking Native Modules

This is where React Native testing diverges most from web testing. Native modules (camera, location, secure storage, biometrics) do not exist in the Jest test environment. You must provide mock implementations that simulate their behavior. The pattern is consistent: create a file in a __mocks__ directory that matches the module’s import path, and export mock functions that return predictable values.
Practical tip: Keep a __mocks__ directory at your project root and commit it to version control. When a teammate adds a new native dependency and the tests break, they can check this directory for the pattern to follow. A well-maintained mock directory is one of the highest-leverage testing investments you can make.

Common Mocks

Testing with Mocked Modules


Snapshot Testing

Snapshot Testing Best Practices:
  • Use sparingly - they can become maintenance burden
  • Keep snapshots small and focused
  • Review snapshot changes carefully in PRs
  • Consider inline snapshots for small components

Testing Utilities

Custom Render Function

Test Data Factories


Code Coverage

Running Coverage

Coverage Configuration


Choosing the Right Testing Approach

Different parts of your app demand different testing strategies. Spending the same effort on every component leads to either under-tested critical paths or over-tested presentational components. Decision framework for what to test first:
  1. Start with business logic and hooks. These are fast to test, stable, and catch the bugs that actually matter (wrong calculations, broken state transitions, malformed API payloads).
  2. Add behavioral tests for user-interactive components. Forms, modals, and multi-step flows deserve thorough testing because bugs here directly block users.
  3. Use snapshots only for “golden” components. If a component’s visual output is its contract (e.g., a design system button), a snapshot is appropriate. For everything else, behavioral assertions are more resilient.
  4. Save E2E tests for critical paths. Login, checkout, and data-destructive flows. These tests are expensive to write and maintain — invest only where a failure in production is catastrophic.

Testing React Query Hooks

Testing components that use React Query requires wrapping them in a QueryClientProvider with specific test configuration. This is a common stumbling point because the default QueryClient retries failed queries (which makes test failures confusing and slow).

Edge Cases in React Native Testing

Testing Animated Components

Components that use react-native-reanimated require special mock setup. Animations run on a separate thread and do not execute in the Jest environment. The common mistake is trying to assert on animated values — they will always be at their initial state in tests.

Testing Components with Timers

jest.useFakeTimers() can cause subtle issues with React Native’s event loop and async operations. The most common trap: waitFor from RNTL uses real timers internally, so calling jest.advanceTimersByTime() inside a waitFor creates a deadlock.

Testing Platform-Specific Behavior

React Native components often branch on Platform.OS. To test both branches, you need to mock the Platform module per test, which requires jest.resetModules() because Platform is cached.

Best Practices

Test Behavior, Not Implementation

Focus on what the component does, not how it does it

Use Accessible Queries

Prefer getByRole, getByLabelText over getByTestId

Avoid Testing Implementation Details

Don’t test internal state or private methods

Keep Tests Independent

Each test should be able to run in isolation

Query Priority

Use the most user-centric query first. This ordering ensures your tests break only when user-visible behavior changes, not when you rename a CSS class or restructure a component tree:

Coverage Targets That Actually Help

A blanket “80% coverage” mandate is worse than targeted coverage requirements. Here is a more useful breakdown:

Next Steps

Module 28: Integration Testing

Learn to test component interactions and navigation flows