测试 React Native 应用
在 Facebook,我们使用 Jest 来测试 React Native 应用。
通过阅读以下系列文章,更深入地了解如何测试一个工作的 React Native 应用示例:第 1 部分:Jest - 快照开始发挥作用 和 第 2 部分:Jest - Redux 快照用于您的操作和 reducer。
设置
从 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
。
快照测试
让我们为一个包含一些视图和文本组件以及一些样式的小型介绍组件创建一个 快照测试
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 的快照功能来与组件交互,捕获渲染的输出并创建一个快照文件
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 test
或 jest
时,这将生成一个类似于此的输出文件
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
调用 Jest 来覆盖现有的快照。
此示例的代码可在 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|react-native-button)/)"
]
}
您可以使用工具 像这样 测试哪些路径会匹配(因此将从转换中排除)。
transformIgnorePatterns
将在路径与提供的任何模式匹配时从转换中排除文件。因此,如果您不小心,将模式分成多个模式可能会产生意想不到的结果。在下面的示例中,对 foo
和 bar
的排除(也称为负向先行断言)相互抵消
{
"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');
这将使用其所有 props 在快照输出中将组件渲染为 <Video {...props} />
。另请参阅 有关 Enzyme 和 React 16 的注意事项。
有时您需要提供更复杂的手动模拟。例如,如果您想将原生组件的 prop 类型或静态字段转发到模拟,您可以通过来自 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
列表中。