使用 Jest 进行单元测试

了解如何设置和配置 jest-expo 库以使用 Jest 为项目编写单元和快照测试。


Jest 是最广泛使用的单元和快照 JavaScript 测试框架。在本指南中,你将学习如何在项目中设置 Jest、编写单元测试、编写快照测试,以及在将 Jest 与 React Native 结合使用时构建测试的最佳实践。

¥Jest is the most widely used unit and snapshot JavaScript testing framework. In this guide, you will learn how to set up Jest in your project, write a unit test, write a snapshot test, and best practices for structuring your tests when using Jest with React Native.

你还将使用 jest-expo 库,这是一个 Jest 预设,可模拟 Expo SDK 的原生部分并处理 Expo 项目所需的大部分配置。

¥You will also use the jest-expo library, which is a Jest preset that mocks the native part of the Expo SDK and handles most of the configuration required for your Expo project.

安装和配置

¥Installation and configuration

如果你使用 默认 Expo 模板 创建了项目,则可以跳过此部分。jest-expo 和其他所需的开发依赖已安装并配置。

¥If you have created your project using the default Expo template, you can skip this section. The jest-expo and other required dev dependencies are already installed and configured.

Manual installation instructions for jest-expo

If you have created your project using different template, follow the instructions below to install and configure jest-expo in your project.

1

Install jest-expo and other required dev dependencies in your project. Run the following command from your project's root directory:

Terminal
npx expo install jest-expo jest @types/jest --dev
Terminal
npx expo install jest-expo jest @types/jest "--" --dev

Note: If your project is not using TypeScript, you can skip installing @types/jest.

2

Open package.json, add a script for running tests, and add the preset for using the base configuration from jest-expo:

package.json
{
  "scripts": {
    "test": "jest --watchAll"
    %%placeholder-start%%... %%placeholder-end%%},
  "jest": {
    "preset": "jest-expo"
  }
}
Additional configuration for using transformIgnorePatterns

You can transpile node modules your project uses by configuring transformIgnorePatterns in your package.json. This property takes a regex pattern as its value:

package.json
"jest": {
  "preset": "jest-expo",
  "transformIgnorePatterns": [
    "node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg)"
  ]
}
package.json
"jest": {
  "preset": "jest-expo",
  "transformIgnorePatterns": [
    "node_modules/(?!(?:.pnpm/)?((jest-)?react-native|@react-native(-community)?|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg))"
  ]
}

Jest has many configuration options, but the above configuration should cover most of your needs. However, you can always add to this pattern list. For more details, see Configuring Jest.

Install React Native Testing Library

The React Native Testing Library (@testing-library/react-native) is a lightweight solution for testing React Native components. It provides utility functions and works with Jest.

To install it, run the following command:

Terminal
npx expo install @testing-library/react-native --dev
Terminal
npx expo install @testing-library/react-native "--" --dev
Deprecated: If you are using the default Expo template, after installing this library, you can uninstall the react-test-renderer and @types/react-test-renderer from your project's dev dependencies. The react-test-renderer has been deprecated and will no longer be maintained in the future. See React's documentation for more information.

Unit test

A unit test checks the smallest unit of code, usually a function. To write your first unit test, take a look at the following example:

1

Inside the app directory of your project, create a new file called index.tsx, and the following code to render a simple component:

index.tsx
import { PropsWithChildren } from 'react';
import { StyleSheet, Text, View } from 'react-native';

export const CustomText = ({ children }: PropsWithChildren) => <Text>{children}</Text>;

export default function HomeScreen() {
  return (
    <View style={styles.container}>
      <CustomText>Welcome!</CustomText>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

2

Create a __tests__ directory at the root of your project's directory. If this directory already exists in your project, use that. Then, create a new file called HomeScreen-test.tsx. The jest-expo preset customizes the Jest configuration to also identify files with -test.ts|tsx extensions as tests.

Add the following example code in HomeScreen-test.tsx:

HomeScreen-test.tsx
import { render } from '@testing-library/react-native';

import HomeScreen, { CustomText } from '@/app/index';

describe('<HomeScreen />', () => {
  test('Text renders correctly on HomeScreen', () => {
    const { getByText } = render(<HomeScreen />);

    getByText('Welcome!');
  });
});

In the above example, the getByText query helps your tests find relevant element in your app's user interface and make assertion whether or not the certain element exists. The React Native Testing Library provides this query, and each query variant differs in its return type. For more examples and detailed API information, see the React Native Testing Library's Queries API reference.

3

Run the following command in a terminal window to execute the test:

Terminal
npm run test

You will see one test being passed.

Structure your tests

Organizing your test files is important to make them easier to maintain. A common pattern is creating a __tests__ directory and putting all your tests inside.

An example structure of tests next to the components directory is shown below:

__tests__
ThemedText-test.tsx
components
ThemedText.tsx
ThemedView.tsx

Alternatively, you can have multiple __tests__ sub-directories for different areas of your project. For example, create a separate test directory for components, and so on:

components
ThemedText.tsx
__tests__
  ThemedText-test.tsx
utils
index.tsx
__tests__
  index-test.tsx

It's all about preferences, and it is up to you to decide how you want to organize your project directory.

Snapshot test

A snapshot test is used to make sure that UI stays consistent, especially when a project is working with global styles that are potentially shared across components.

To add a snapshot test for <HomeScreen />, add the following code snippet in the describe() in HomeScreen-test.tsx:

HomeScreen-test.tsx
describe('<HomeScreen />', () => {
  %%placeholder-start%%... %%placeholder-end%%

  test('CustomText renders correctly', () => {
    const tree = render(<CustomText>Some text</CustomText>).toJSON();

    expect(tree).toMatchSnapshot();
  });
});

Run npm run test command, and you will see a snapshot created inside __tests__\__snapshots__ directory, and two tests passed.

Code coverage reports

Code coverage reports can help you understand how much of your code is tested. To see the code coverage report in your project using the HTML format, in package.json, under jest, set the collectCoverage to true and use collectCoverageFrom to specify a list of files to ignore when collecting the coverage.

package.json
"jest": {
  ...
  "collectCoverage": true,
  "collectCoverageFrom": [
    "**/*.{ts,tsx,js,jsx}",
    "!**/coverage/**",
    "!**/node_modules/**",
    "!**/babel.config.js",
    "!**/expo-env.d.ts",
    "!**/.expo/**"
  ]
}

Run npm run test. You will see a coverage directory created in your project. Find the lcov-report/index.html and open it in a browser to see the coverage report.

通常,我们不建议将 index.html 文件上传到 git。在 .gitignore 文件中添加 coverage/**/* 以防止被跟踪。

¥Usually, we don't recommend uploading index.html file to git. Add coverage/**/* in the .gitignore file to prevent it from being tracked.

Jest 流程(可选)

¥Jest flows (optional)

你还可以使用不同的流程来运行测试。以下是你可以尝试的一些示例脚本:

¥You can also use different flows to run your tests. Below are a few example scripts that you can try:

package.json
"scripts": {
  "test": "jest --watch --coverage=false --changedSince=origin/main",
  "testDebug": "jest -o --watch --coverage=false",
  "testFinal": "jest",
  "updateSnapshots": "jest -u --coverage=false"
  %%placeholder-start%%... %%placeholder-end%%
}

有关更多信息,请参阅 Jest 文档中的 CLI 选项

¥For more information, see CLI Options in Jest documentation.

附加信息

¥Additional information

React Native 测试库文档

请参阅 React Native Testing Library 文档,该文档提供测试实用程序并鼓励良好的测试实践和使用 Jest。

测试 Expo Router 的配置

了解如何在使用 Expo Router 时为你的应用创建集成测试。

使用 EAS 工作流进行端到端测试

了解如何使用 Maestro 在 EAS 工作流上设置和运行端到端测试。