0Pricing
React Native Academy · 课时

在 React Native 项目中设置 Jest

使用 react-native 预设配置 Jest,运行第一个测试文件,了解 test、describe 和 expect API,并设置代码覆盖率报告。

在 React Native 项目中设置 Jest 是 CoddyKit 上的免费 React Native Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Native Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Native Academy 课程共包含 4 节课。

本课时的部分内容尚未翻译,以英文显示。

Why Testing React Native Code

Tests are your safety net when making changes. A well-written test suite lets you refactor confidently, catch regressions before they reach users, and document expected behavior for your teammates. For React Native specifically, tests are especially valuable because running the full app on a simulator is slow compared to running unit tests.

There are three levels of testing in React Native: unit tests (individual functions and hooks), component tests (rendering components and asserting on output), and end-to-end tests (driving the real app on a device). Jest handles the first two.

Jest Comes Pre-Configured

React Native projects created with the React Native CLI or Expo SDK already include Jest. You will find jest in devDependencies and a Jest configuration in package.json under the 'jest' key, or in a jest.config.js file.

The preset 'react-native' (from @react-native/jest-preset) or 'jest-expo' (for Expo) configures Jest to transform JSX and handle native module mocks automatically. Always use the appropriate preset for your project type.

// package.json (React Native CLI project)
{
  'jest': {
    'preset': 'react-native'
  }
}

// package.json (Expo project)
{
  'jest': {
    'preset': 'jest-expo',
    'transformIgnorePatterns': [
      'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*)'
    ]
  }
}

Running Your First Test

Run tests with npx jest or just yarn test. Jest finds all files matching *.test.ts, *.test.tsx, *.spec.ts, or files inside __tests__ folders. Add --watch to rerun tests automatically when files change — this is the recommended workflow during development.

A newly scaffolded project includes a sample test in App.test.tsx. Running it confirms your Jest setup is working before you write your own tests.

# Run all tests once
npx jest

# Run in watch mode (rerun on file change)
npx jest --watch

# Run a specific test file
npx jest src/utils/formatDate.test.ts

# Run tests matching a pattern
npx jest --testNamePattern='should format'

The Anatomy of a Test

A Jest test file uses three core functions: describe groups related tests, it (or test) defines a single test case, and expect makes assertions about values. The pattern is: arrange (set up data), act (run the code), assert (verify the output).

Test names should describe the expected behavior in plain English. Good test names read like documentation: 'should return null when the input is empty' is clearer than 'test1'.

// src/utils/formatDate.test.ts
import { formatDate } from './formatDate';

describe('formatDate', () => {
  it('should format a date as MMM DD, YYYY', () => {
    // Arrange
    const date = new Date('2024-03-15T10:00:00Z');

    // Act
    const result = formatDate(date);

    // Assert
    expect(result).toBe('Mar 15, 2024');
  });

  it('should return null when the input is null', () => {
    expect(formatDate(null)).toBeNull();
  });
});

Common Jest Matchers

Jest provides a rich set of matchers — methods on the expect object that check values in different ways. The most common matchers for React Native tests are:

  • toBe(value) — strict equality with ===
  • toEqual(value) — deep equality for objects and arrays
  • toBeTruthy() / toBeFalsy() — truthy/falsy checks
  • toContain(item) — array contains item
  • toThrow() — function throws an error
// Strict equality (for primitives)
expect(2 + 2).toBe(4);

// Deep equality (for objects)
expect({ name: 'Alice', age: 30 }).toEqual({ name: 'Alice', age: 30 });

// Array checks
expect(['a', 'b', 'c']).toContain('b');
expect(['a', 'b', 'c']).toHaveLength(3);

// Null / undefined
expect(null).toBeNull();
expect(undefined).toBeUndefined();

// Error throwing
expect(() => JSON.parse('invalid')).toThrow(SyntaxError);

Testing Pure Functions

The easiest things to test are pure functions — functions that take input, return output, and have no side effects. Examples include validation functions, data transformers, and utility helpers. These tests are fast and require no mocking.

Write one test per edge case. Test the happy path (valid input), boundary conditions (empty string, zero, null), and error cases (invalid input). This is where your test-to-code ratio should be highest.

// src/utils/validators.ts
export function isValidEmail(email: string): boolean {
  return /^[^@]+@[^@]+\.[^@]+$/.test(email);
}

// src/utils/validators.test.ts
import { isValidEmail } from './validators';

describe('isValidEmail', () => {
  it('should return true for a valid email', () => {
    expect(isValidEmail('user@example.com')).toBe(true);
  });

  it('should return false for an email without @', () => {
    expect(isValidEmail('notanemail.com')).toBe(false);
  });

  it('should return false for an empty string', () => {
    expect(isValidEmail('')).toBe(false);
  });
});

Mocking with jest.fn()

jest.fn() creates a mock function — a fake implementation that records how it was called. You pass mock functions as props to components or inject them as dependencies to isolate the code under test from its real collaborators.

After running the code under test, assert on the mock using matchers like toHaveBeenCalled(), toHaveBeenCalledWith(args), and toHaveBeenCalledTimes(n). This lets you verify that your code correctly calls external APIs or callbacks without actually making network requests.

// Test that a submit handler calls the API with the correct arguments
const mockSubmit = jest.fn().mockResolvedValue({ success: true });

await handleFormSubmit({ email: 'user@test.com' }, mockSubmit);

expect(mockSubmit).toHaveBeenCalledTimes(1);
expect(mockSubmit).toHaveBeenCalledWith({ email: 'user@test.com' });

// Mock return values
const mockFetch = jest.fn().mockResolvedValue([{ id: '1', title: 'Post' }]);
const posts = await fetchPosts(mockFetch);
expect(posts).toHaveLength(1);

Module Mocking with jest.mock()

jest.mock('module-name') replaces an entire module with an auto-mocked version where all exported functions become jest.fn(). This is essential for mocking native modules, which cannot run in the Jest Node.js environment.

Call jest.mock() at the top of the test file. Jest hoists mock calls to the top of the file automatically, so they are set up before any imports run. Use jest.requireActual to preserve parts of the real module.

// Mock expo-location
jest.mock('expo-location', () => ({
  requestForegroundPermissionsAsync: jest.fn().mockResolvedValue({ status: 'granted' }),
  getCurrentPositionAsync: jest.fn().mockResolvedValue({
    coords: { latitude: 37.78825, longitude: -122.4324 },
  }),
}));

// Mock React Navigation
jest.mock('@react-navigation/native', () => ({
  ...jest.requireActual('@react-navigation/native'),
  useNavigation: () => ({ navigate: jest.fn() }),
  useRoute: () => ({ params: { userId: '123' } }),
}));

Code Coverage Reports

Jest can measure code coverage — what percentage of your code is executed by tests. Run jest --coverage to generate a coverage report in the terminal and an HTML report in coverage/lcov-report/index.html.

The report shows four metrics per file: Statements (lines of code executed), Branches (if/else paths taken), Functions (functions called), and Lines. Focus on covering critical business logic, error handling paths, and utility functions — 80-90% coverage on those is more valuable than 100% coverage on simple render components.

# Run tests with coverage report
npx jest --coverage

# Coverage thresholds in jest.config.js
module.exports = {
  coverageThreshold: {
    global: {
      statements: 80,
      branches: 70,
      functions: 80,
      lines: 80,
    },
  },
};

Setup and Teardown with beforeEach and afterEach

beforeEach and afterEach run setup and cleanup code around each test in a describe block. Use beforeEach to reset mocks, initialize test data, or create a fresh component instance. Use afterEach to clean up timers, subscriptions, or async operations.

beforeAll and afterAll run once before/after all tests in a describe block — useful for expensive setup like opening a database connection that is shared across tests.

describe('AuthService', () => {
  let mockStorage: Record<string, string>;

  beforeEach(() => {
    // Fresh state before each test
    mockStorage = {};
    jest.clearAllMocks(); // Reset mock call counts
  });

  afterEach(() => {
    // Clean up any timers started during the test
    jest.useRealTimers();
  });

  it('should save the token to storage on sign in', async () => {
    await signIn('user@test.com', 'password', mockStorage);
    expect(mockStorage['authToken']).toBeDefined();
  });
});

Testing Async Code with async/await

Most React Native operations are asynchronous. Jest supports async/await directly in test functions. Return the promise (or use async/await) so Jest knows to wait for it to resolve before marking the test as passed.

For code that uses setTimeout or setInterval, use jest.useFakeTimers() to control time in tests. Call jest.runAllTimers() to fast-forward all pending timers without actually waiting.

// Testing async functions
it('should return sorted posts from the API', async () => {
  const posts = await fetchAndSortPosts();
  expect(posts[0].createdAt).toBeGreaterThan(posts[1].createdAt);
});

// Testing code with timers
it('should call the callback after 1 second', () => {
  jest.useFakeTimers();
  const callback = jest.fn();

  scheduleCallback(callback, 1000);
  expect(callback).not.toHaveBeenCalled();

  jest.advanceTimersByTime(1000);
  expect(callback).toHaveBeenCalledTimes(1);
});

Quick Check

Test your understanding of React Native Mobile Development concepts from this lesson.

Lesson Recap

In this lesson you learned: how Jest is configured in React Native and Expo projects via presets, how to write unit tests for pure functions using the arrange-act-assert pattern, and how to mock functions and modules with jest.fn() and jest.mock() to isolate code under test. Next up we render React Native components in tests using React Native Testing Library.

常见问题解答

「在 React Native 项目中设置 Jest」课时是免费的吗?

是的 — 「在 React Native 项目中设置 Jest」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Native Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Native Academy 课程共包含 4 节课。

「在 React Native 项目中设置 Jest」这节课中我会学到什么?

使用 react-native 预设配置 Jest,运行第一个测试文件,了解 test、describe 和 expect API,并设置代码覆盖率报告。 你通过在浏览器中直接运行的动手代码来练习 React Native Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 React Native Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 React Native Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「在 React Native 项目中设置 Jest」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 React Native Academy 课中编写并运行代码吗?

能。每节 React Native Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 在 React Native 项目中设置 Jest
  2. 使用 React Native Testing Library 渲染组件
  3. 触发事件与测试用户交互
  4. 模拟原生模块与异步代码
← 返回 React Native Academy