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 applesimutilsjest.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();
});
});