跳至主内容
版本:30.0

测试 React Native 应用

非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

在 Facebook,我们使用 Jest 测试 React Native 应用。

通过阅读以下系列文章深入了解实际 React Native 应用的测试实践:第一部分:Jest - 快照测试实战第二部分:Jest - Redux 操作与还原器的快照测试

环境配置

从 react-native 0.38 版本开始,运行 react-native init 时会默认包含 Jest 配置。以下配置将自动添加到您的 package.json 文件中:

{
"scripts": {
"test": "jest"
},
"jest": {
"preset": "react-native"
}
}

运行 yarn test 即可使用 Jest 执行测试。

技巧

如果您正在升级 React Native 应用,且之前使用了 jest-react-native 预设,请从 package.json 中移除该依赖,并将预设改为 react-native

快照测试

让我们为一个简单的介绍组件创建快照测试,该组件包含若干视图、文本组件和样式:

Intro.js
import React, {Component} from 'react';
import {StyleSheet, Text, View} from 'react-native';

class Intro extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>
This is a React Native snapshot test.
</Text>
</View>
);
}
}

const styles = StyleSheet.create({
container: {
alignItems: 'center',
backgroundColor: '#F5FCFF',
flex: 1,
justifyContent: 'center',
},
instructions: {
color: '#333333',
marginBottom: 5,
textAlign: 'center',
},
welcome: {
fontSize: 20,
margin: 10,
textAlign: 'center',
},
});

export default Intro;

现在使用 React 的测试渲染器和 Jest 的快照功能与组件交互,捕获渲染输出并创建快照文件:

__tests__/Intro-test.js
import React from 'react';
import renderer from 'react-test-renderer';
import Intro from '../Intro';

test('renders correctly', () => {
const tree = renderer.create(<Intro />).toJSON();
expect(tree).toMatchSnapshot();
});

当您运行 yarn testjest 时,将生成如下输出文件:

__tests__/__snapshots__/Intro-test.js.snap
exports[`Intro renders correctly 1`] = `
<View
style={
Object {
"alignItems": "center",
"backgroundColor": "#F5FCFF",
"flex": 1,
"justifyContent": "center",
}
}>
<Text
style={
Object {
"fontSize": 20,
"margin": 10,
"textAlign": "center",
}
}>
Welcome to React Native!
</Text>
<Text
style={
Object {
"color": "#333333",
"marginBottom": 5,
"textAlign": "center",
}
}>
This is a React Native snapshot test.
</Text>
</View>
`;

下次运行测试时,渲染输出会与先前创建的快照进行比较。快照文件应随代码变更一并提交。当快照测试失败时,您需要检查这是预期变更还是意外变更。如果是预期变更,可通过 jest -u 命令覆盖现有快照。

此示例代码可在 examples/react-native 查看。

预设配置

该预设会配置测试环境,其设计基于 Facebook 的经验总结,具有较强倾向性。所有配置选项均可覆盖,就像不使用预设时那样进行自定义。

环境配置

react-native 内置了 Jest 预设,因此您 package.json 中的 jest.preset 字段应指向 react-native。该预设模拟了 React Native 应用环境,由于不加载任何 DOM 或浏览器 API,能显著提升 Jest 的启动速度。

transformIgnorePatterns 自定义

transformIgnorePatterns 选项用于指定哪些文件需要经过 Babel 转换。遗憾的是许多 react-native npm 模块在发布前未预编译源代码。

默认情况下,jest-react-native 预设仅处理项目自身源码和 react-native。如需转换其他 npm 依赖,可通过包含 react-native 以外模块并使用 | 操作符分组来自定义配置:

{
"transformIgnorePatterns": ["node_modules/(?!((@)?react-native|my-project)/)"]
}

您可以使用此类工具测试哪些路径会被匹配(从而被排除在转换范围外)。

当文件路径匹配任意提供的模式时,transformIgnorePatterns 会将其排除在转换范围外。因此若未谨慎处理,拆分为多个模式可能导致意外结果。下例中针对 foobar 的排除规则(称为负向先行断言)会相互抵消:

{
"transformIgnorePatterns": ["node_modules/(?!foo/)", "node_modules/(?!bar/)"] // not what you want
}

setupFiles

若需为每个测试文件提供额外配置,可通过 setupFiles 配置选项指定初始化脚本。

moduleNameMapper

moduleNameMapper 可用于将模块路径映射到不同的模块。默认预设会将所有图片映射到图片存根模块,但当某个模块无法找到时,此配置选项可以提供帮助:

{
"moduleNameMapper": {
"my-module.js": "<rootDir>/path/to/my-module.js"
}
}

提示

使用 jest.mock 模拟原生模块

react-native 内置的 Jest 预设附带了一些默认模拟,会应用于 react-native 仓库。然而,某些 react-native 组件或第三方组件需要依赖原生代码才能渲染。这种情况下,Jest 的手动模拟系统可以帮助模拟底层实现。

例如,如果你的代码依赖名为 react-native-video 的第三方原生视频组件,可能需要使用如下手动模拟进行存根:

jest.mock('react-native-video', () => 'Video');

这会将组件渲染为 <Video {...props} /> 并在快照输出中包含其所有属性。另请参阅使用 Enzyme 和 React 16 进行模拟快照测试的注意事项

有时你需要提供更复杂的手动模拟。例如,若希望将原生组件的属性类型或静态字段转发到模拟,可以通过 jest-react-native 的这个辅助函数从模拟返回不同的 React 组件:

jest.mock('path/to/MyNativeComponent', () => {
const mockComponent = require('react-native/jest/mockComponent');
return mockComponent('path/to/MyNativeComponent');
});

或者,如果你想创建自己的手动模拟,你可以这样做:

jest.mock('Text', () => {
const RealComponent = jest.requireActual('Text');
const React = require('react');
class Text extends React.Component {
render() {
return React.createElement('Text', this.props, this.props.children);
}
}
Text.propTypes = RealComponent.propTypes;
return Text;
});

在其他情况下,你可能需要模拟非 React 组件的原生模块。同样适用此技术。我们建议检查原生模块的源代码,并在真实设备上运行 react native 应用时记录该模块日志,然后根据真实模块构建手动模拟。

如果发现反复模拟相同模块,建议将这些模拟定义在单独文件中,并添加到 setupFiles 列表中。