ScaleRocket/Mobile

Testing

Testing mobile apps with Jest, React Native Testing Library, Detox for E2E, and simulator vs real device testing.

Testing

ScaleRocket Mobile supports unit testing with Jest and React Native Testing Library, plus end-to-end testing with Detox for critical user flows.

Test Stack

ToolTypeTests
JestUnit & integrationFunctions, hooks, logic
React Native Testing LibraryComponentUI rendering, interactions
DetoxEnd-to-endFull user flows on simulator

Setup

Jest and React Native Testing Library are pre-configured. Install if starting fresh:

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

Configure in package.json:

{
  "jest": {
    "preset": "jest-expo",
    "setupFilesAfterSetup": ["@testing-library/jest-native/extend-expect"]
  }
}

Unit Tests

Test utility functions and hooks:

// lib/__tests__/utils.test.ts
import { formatCurrency, validateEmail } from "../utils";

describe("formatCurrency", () => {
  it("formats USD correctly", () => {
    expect(formatCurrency(9.99, "USD")).toBe("$9.99");
  });

  it("handles zero", () => {
    expect(formatCurrency(0, "USD")).toBe("$0.00");
  });
});

describe("validateEmail", () => {
  it("accepts valid emails", () => {
    expect(validateEmail("user@example.com")).toBe(true);
  });

  it("rejects invalid emails", () => {
    expect(validateEmail("not-an-email")).toBe(false);
  });
});

Component Tests

Test components render correctly and respond to user interaction:

// components/__tests__/Button.test.tsx
import { render, fireEvent } from "@testing-library/react-native";
import { Button } from "../ui/Button";

describe("Button", () => {
  it("renders the title", () => {
    const { getByText } = render(
      <Button title="Submit" onPress={() => {}} />
    );
    expect(getByText("Submit")).toBeTruthy();
  });

  it("calls onPress when pressed", () => {
    const onPress = jest.fn();
    const { getByText } = render(
      <Button title="Submit" onPress={onPress} />
    );
    fireEvent.press(getByText("Submit"));
    expect(onPress).toHaveBeenCalledTimes(1);
  });

  it("does not call onPress when disabled", () => {
    const onPress = jest.fn();
    const { getByText } = render(
      <Button title="Submit" onPress={onPress} disabled />
    );
    fireEvent.press(getByText("Submit"));
    expect(onPress).not.toHaveBeenCalled();
  });

  it("shows loading indicator when loading", () => {
    const { getByTestId, queryByText } = render(
      <Button title="Submit" onPress={() => {}} loading />
    );
    expect(getByTestId("loading-indicator")).toBeTruthy();
    expect(queryByText("Submit")).toBeNull();
  });
});

Testing Hooks

Test custom hooks with renderHook:

import { renderHook, act } from "@testing-library/react-native";
import { useCounter } from "../hooks/useCounter";

describe("useCounter", () => {
  it("increments count", () => {
    const { result } = renderHook(() => useCounter());

    act(() => {
      result.current.increment();
    });

    expect(result.current.count).toBe(1);
  });
});

Mocking Native Modules

Mock Expo and React Native modules that don't work in the test environment:

// jest.setup.ts
jest.mock("expo-secure-store", () => ({
  getItemAsync: jest.fn(),
  setItemAsync: jest.fn(),
  deleteItemAsync: jest.fn(),
}));

jest.mock("@react-native-async-storage/async-storage", () =>
  require("@react-native-async-storage/async-storage/jest/async-storage-mock")
);

E2E Testing with Detox

For critical user flows (login, purchase, onboarding), use Detox:

npm install --save-dev detox
// e2e/login.test.ts
describe("Login Flow", () => {
  beforeAll(async () => {
    await device.launchApp();
  });

  it("should login with valid credentials", async () => {
    await element(by.id("email-input")).typeText("user@example.com");
    await element(by.id("password-input")).typeText("password123");
    await element(by.id("login-button")).tap();

    await expect(element(by.text("Dashboard"))).toBeVisible();
  });
});

Simulator vs Real Device

AspectSimulator/EmulatorReal Device
SpeedFastSlower to deploy
BiometricsSimulatedReal Face ID/fingerprint
Push notificationsLimitedFull support
PerformanceNot representativeAccurate
CameraNot availableWorks

Recommendation: Use simulators for development and automated tests. Test on real devices before submitting to app stores.

Running Tests

# Run all tests
npm test

# Run with coverage
npm test -- --coverage

# Run specific test file
npm test -- Button.test

# Watch mode
npm test -- --watch

On this page