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
| Tool | Type | Tests |
|---|---|---|
| Jest | Unit & integration | Functions, hooks, logic |
| React Native Testing Library | Component | UI rendering, interactions |
| Detox | End-to-end | Full 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-nativeConfigure 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
| Aspect | Simulator/Emulator | Real Device |
|---|---|---|
| Speed | Fast | Slower to deploy |
| Biometrics | Simulated | Real Face ID/fingerprint |
| Push notifications | Limited | Full support |
| Performance | Not representative | Accurate |
| Camera | Not available | Works |
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