iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Testing (Jest, Detox)

Testing React Native covers three layers: unit (Jest), component (React Native Testing Library), and end-to-end (Detox or Maestro). Each catches a different class of bug; together they cover what a manual smoke test would do without the manual part.

Jest + RNTL + Maestro examples

EXAMPLE
// 1) Setup — jest preset
// package.json
// {
//   "jest": {
//     "preset": "react-native",
//     "setupFilesAfterEach": ["@testing-library/jest-native/extend-expect"]
//   }
// }
// npm i -D jest @testing-library/react-native @testing-library/jest-native

// 2) Pure logic — unit test (Jest)
// src/lib/cents.ts
export function formatCents(cents: number, currency = 'AUD') {
  return new Intl.NumberFormat('en-AU', { style: 'currency', currency }).format(cents / 100);
}

// src/lib/cents.test.ts
import { formatCents } from './cents';
describe('formatCents', () => {
  it('rounds to two decimals', () => {
    expect(formatCents(4995)).toBe('A$49.95');
  });
  it('handles other currencies', () => {
    expect(formatCents(4995, 'USD')).toBe('USD49.95');
  });
});

// 3) Component test — React Native Testing Library
// src/components/Counter.tsx
import { Text, Pressable, View } from 'react-native';
import { useState } from 'react';
export function Counter() {
  const [n, setN] = useState(0);
  return (
    <View>
      <Text testID='count'>Count: {n}</Text>
      <Pressable accessibilityRole='button' onPress={() => setN((x) => x + 1)}>
        <Text>Increment</Text>
      </Pressable>
    </View>
  );
}

// src/components/Counter.test.tsx
import { render, fireEvent, screen } from '@testing-library/react-native';
import { Counter } from './Counter';

it('increments on press', () => {
  render(<Counter />);
  expect(screen.getByTestId('count')).toHaveTextContent('Count: 0');
  fireEvent.press(screen.getByRole('button', { name: /increment/i }));
  expect(screen.getByTestId('count')).toHaveTextContent('Count: 1');
});

// 4) Mocking native modules
// __mocks__/react-native-async-storage-async-storage.js
// jest.mock('@react-native-async-storage/async-storage', () => ({
//   getItem: jest.fn(() => Promise.resolve(null)),
//   setItem: jest.fn(() => Promise.resolve()),
// }));

// 5) Snapshot a screen carefully — avoid brittle UI snapshots
import renderer from 'react-test-renderer';
it('matches snapshot', () => {
  const tree = renderer.create(<Counter />).toJSON();
  expect(tree).toMatchSnapshot();
});
// Treat snapshot failures as 'review the diff'; do not auto-update.

// 6) End-to-end with Maestro (recommended over Detox for new projects)
// flow.yaml
// appId: au.com.example.shop
// ---
// - launchApp
// - assertVisible: 'Welcome'
// - tapOn: 'Sign in'
// - inputText: 'alice@example.com'
// - tapOn: 'Continue'
// - inputText: 'hunter2'
// - tapOn: 'Sign in'
// - assertVisible: 'Catalog'
//
// Run on a real device or simulator:
// maestro test flow.yaml

// 7) End-to-end with Detox (heavier; tied to a built binary)
// detox.config.js → describes simulators / emulators
// e2e/sample.e2e.ts
// describe('Catalog', () => {
//   it('shows products', async () => {
//     await element(by.text('Catalog')).tap();
//     await expect(element(by.text('Wool jacket'))).toBeVisible();
//   });
// });

// 8) Test ID and accessibility
// - Prefer accessibilityRole / accessibilityLabel queries (testing accessibility for free)
// - Fall back to testID for opaque elements
// - Never query by text that is translated; use a stable key

// 9) Run in CI
// GitHub Actions matrix:
// - jest unit + component tests on every PR
// - Maestro on an emulator job for main branch deploys
// - Skip slow e2e in PRs; run a smaller smoke set instead

// 10) Pitfalls
// - Snapshot-heavy test suites: brittle, slow to update, low value
// - No mocks of network → tests hit production
// - Detox tests that depend on hard-coded device timing
// - Skipping accessibility queries → tests pass while screen-readers cannot use the app

Why it matters

Build the testing pyramid: many unit tests, fewer component tests, very few e2e tests. The unit tests catch logic bugs in milliseconds; component tests prove the UI wires up; e2e exists to confirm "the whole thing still works" on a real device once per deploy. Inverting the pyramid leads to slow, flaky CI that nobody trusts.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
# Unit tests
npm install -D jest @testing-library/react-native
# End-to-end
npm install -D detox
detox test
Try it Yourself »

Discussion

Loading…