R
Rishtaara
React Native Advanced: CI/CD Pipeline Mastery
Lesson 11 of 19Article22 min

Jest (Unit) & Detox (E2E) Project Setup

Unit tests (Jest) are fast and run on every PR. Integration tests cover navigation and API layers. E2E tests (Detox) validate critical user journeys on real simulators — run nightly or pre-release due to cost.

Testing pyramid for React Native

Unit tests (Jest) are fast and run on every PR. Integration tests cover navigation and API layers. E2E tests (Detox) validate critical user journeys on real simulators — run nightly or pre-release due to cost.

Aim for 70%+ unit coverage on business logic; 5–10 Detox flows covering login, checkout, and core navigation.

Mobile testing pyramid

Jest unit test setup

Install dependencies
npm install --save-dev jest @testing-library/react-native @types/jest
# Detox (E2E)
npm install --save-dev detox detox-cli
# iOS simulator utils
brew tap wix/brew && brew install applesimutils
jest.config.js
module.exports = {
  preset: "react-native",
  setupFilesAfterSetup: ["@testing-library/jest-native/extend-expect"],
  transformIgnorePatterns: [
    "node_modules/(?!(react-native|@react-native|@react-navigation)/)"
  ],
  collectCoverageFrom: ["src/**/*.{ts,tsx}", "!src/**/*.d.ts"],
};
Sample unit test
import { render, screen } from "@testing-library/react-native";
import { LoginButton } from "./LoginButton";

it("renders login label", () => {
  render(<LoginButton onPress={() => {}} />);
  expect(screen.getByText("Sign In")).toBeTruthy();
});

Detox E2E setup

package.json scripts
{
  "scripts": {
    "test": "jest",
    "detox:build:android": "detox build -c android.emu.debug",
    "detox:test:android": "detox test -c android.emu.debug",
    "detox:build:ios": "detox build -c ios.sim.debug",
    "detox:test:ios": "detox test -c ios.sim.debug"
  }
}
.detoxrc.js
module.exports = {
  apps: {
    "android.debug": {
      type: "android.apk",
      binaryPath: "android/app/build/outputs/apk/debug/app-debug.apk",
      build: "cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug"
    },
    "ios.debug": {
      type: "ios.app",
      binaryPath: "ios/build/Build/Products/Debug-iphonesimulator/MyApp.app",
      build: "xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build"
    }
  },
  devices: {
    emulator: { type: "android.emulator", device: { avdName: "Pixel_6_API_34" } },
    simulator: { type: "ios.simulator", device: { type: "iPhone 15" } }
  },
  configurations: {
    "android.emu.debug": { device: "emulator", app: "android.debug" },
    "ios.sim.debug": { device: "simulator", app: "ios.debug" }
  }
};
Detox E2E test — add testID to components
// App: <TextInput testID="email-input" />
describe("Login flow", () => {
  beforeAll(async () => { await device.launchApp(); });

  it("shows home after valid login", async () => {
    await element(by.id("email-input")).typeText("user@test.com");
    await element(by.id("password-input")).typeText("secret");
    await element(by.id("login-button")).tap();
    await expect(element(by.id("home-screen"))).toBeVisible();
  });
});