Command Palette

Search for a command to run...

React Native Unit Testing Guide with Jest & Testing Library

React Native Unit Testing Guide

Imagine you just finished an extremely complex Shopping Cart feature. You personally swiped, tapped, and added/removed products on your phone and saw everything running perfectly. You confidently pushed the code for your boss.

But the next morning, a flood of error reports came in: "The Checkout feature is broken!". It turns out the code you just added to the Shopping Cart accidentally "killed" the Checkout screen without you even knowing.

This is a developer's nightmare loop: Fix 1 bug, create 3 new bugs.

To end the scenario of having to manually test the entire application every time a line of code changes, large companies apply Automated Testing. And in this lesson, we will get acquainted with the first and most important line of defense: Unit Testing.

1. What is a Unit Test?

In the software testing pyramid, Unit Testing sits at the bottom. It involves writing short snippets of code... to check whether your main code snippets are working exactly as expected.

What is a Unit Test?

Instead of testing the entire application at once, a Unit Test isolates the smallest "unit" (a calculation function, a <Button> tag, a small screen) and tests it independently.

  • Example: You have a function calculateDiscount(price, discount). You will write a Unit Test to check if passing in 100 and 20% correctly returns the number 80.

2. The Perfect Match: Jest & React Native Testing Library

In the React Native ecosystem, there are two dominant tools in the Unit Test area that you must know:

Jest & React Native Testing Library

  1. Jest: This is a Test Runner developed by Facebook. It provides an environment to run code, functions to check results like expect(a).toBe(b), and reports which tests Pass or Fail.
  2. React Native Testing Library: Jest only understands pure JavaScript. To test interfaces (like <View>, <Text>), we need RNTL. This library helps "draw" your components onto a virtual screen in the computer's memory to check if text appears or if a button works.

3. Setting Up the Testing Environment

To get started, open the Terminal in your Expo project folder and install the necessary packages:

npm install --save-dev jest @types/jest @testing-library/react-native react-test-renderer

(Note: We use the --save-dev flag because these test libraries are only used during development and should not be bundled into the actual app to avoid making it heavy).

Next, open the package.json file, find the "scripts" section, and add the test command line:

"scripts": {
  "start": "expo start",
  "test": "jest"
}

4. Testing Function Logic: Jest

Let's start with the easiest thing: Testing a pure JavaScript function.

Step 1: Create the logic file

Create the file src/utils/math.js:

export const add = (a, b) => {
  return a + b
}

Step 2: Write the Test

Create the file src/utils/math.test.js. (Jest will automatically find all files with .test.js to run).

import { add } from './math'

// Declare a group of tests
describe('Testing the math.js file', () => {
  // Declare a specific test (it or test)
  it('The add function should correctly add 2 numbers', () => {
    // 1. Prepare data
    const result = add(2, 3)

    // 2. Expectation
    expect(result).toBe(5) // I expect the result to be 5
  })

  it('The add function handles negative numbers', () => {
    expect(add(-2, 5)).toBe(3)
  })
})

Step 3: Run the test

Open the Terminal and type npm run test. If the screen shows a green PASS, congratulations on successfully writing your first test!

Unit Testing with Jest

5. UI Testing: React Native Testing Library

Testing math functions is easy, but how do we test a button on a phone screen? This is where RNTL shows its skills.

Suppose you have a Button component with a click counter:

// src/components/Counter.tsx
import React, { useState } from 'react'
import { View, Text, TouchableOpacity } from 'react-native'

export default function Counter() {
  const [count, setCount] = useState(0)

  return (
    <View>
      <Text testID="count-display">Clicks: {count}</Text>
      <TouchableOpacity testID="increment-btn" onPress={() => setCount(count + 1)}>
        <Text>Increase</Text>
      </TouchableOpacity>
    </View>
  )
}

(Tip: The testID attribute is like attaching a hidden name tag to View/Text tags, helping RNTL easily find them during tests).

Now, let's write a test for this component in the Counter.test.tsx file:

import React from 'react'
import { render, screen, fireEvent } from '@testing-library/react-native'
import Counter from './Counter'

describe('Testing the Counter component', () => {
  it('Should display the number 0 initially', () => {
    // 1. Render the component onto the virtual screen
    render(<Counter />)

    // 2. Find the text tag via testID
    const textElement = screen.getByTestId('count-display')

    // 3. Check if the content inside contains the number 0
    expect(textElement.props.children.join('')).toContain('Clicks: 0')
  })

  it('Should increase to 1 when the Increase button is pressed', () => {
    render(<Counter />)

    // 1. Find the button and the number display tag
    const button = screen.getByTestId('increment-btn')
    const textElement = screen.getByTestId('count-display')

    // 2. Simulate a user clicking the button (fireEvent)
    fireEvent.press(button)

    // 3. Check the content again, now the expectation should be 1
    expect(textElement.props.children.join('')).toContain('Clicks: 1')
  })
})

Awesome! You just simulated a user tapping the phone screen and the system automatically checking if the number jumps correctly.

6. Practical Advice for Writing Unit Tests

To avoid getting bogged down in writing tests indiscriminately, keep the following principles in mind:

  • Do not test framework internals: You don't need to test if React's useState function or React Native's <View> works correctly. That's Facebook's job. Test your logic.
  • Focus on Complex Logic: Prioritize testing functions that handle currency, calculate scores, format dates, or tangled if/else conditions.
  • Test User Behavior: With RNTL, test like a real user (find buttons, click, see results on the screen); do not try to access implicit State variables inside the component.

Conclusion: Unit Tests are a Solid Shield

Unit Testing is like building the foundation of a house. Initially, you'll feel like writing tests takes twice as long as just writing code. But as your application grows with dozens of intertwined features, Unit Tests are the most solid shield protecting you from sleepless nights fixing bugs!

With the assurance that the code runs smoothly and without errors, we are ready to enter the final stage of any Mobile programming project.

Get ready; in the next lesson, we will personally create the APK/AAB file for Android and configure certificates to create the IPA file for iOS!

Related Posts

React Native Gesture Handler: A Guide to Capturing Swipe and Drag Gestures

Detailed guide on how to use the react-native-gesture-handler library to capture smooth swipe, drag, and drop (Pan, Swipe) gestures without lag on the UI Thread.

React Native Local Data Storage: How to use AsyncStorage & SecureStore

A detailed guide on how to store local data in React Native. Master AsyncStorage for saving settings, SecureStore for secure Tokens, and explore the power of MMKV.

Stack Navigation in Expo Router: Navigating & Passing Parameters

A detailed guide on how to navigate and pass parameters using Stack Navigation in React Native, using Expo Router.

How to use React Native Maps: Displaying Maps & Getting GPS Coordinates

A guide to displaying maps in React Native using the react-native-maps library. Learn how to pin locations (Marker) and integrate real-time GPS.