技术选型
测试工具选型:Jest + testing-library
1、jest 是一个开源的 javascript 单元测试框架,集成了测试执行器、断言库、spy、mock、snapshot和测试覆盖率报告等功能。
2、@testing-library 是用于 Dom 和 UI 组件测试的工具,提供了一系列常用的测试 API。
注意:react-native-testing-library 已经转移到 @test-library/react-native
环境搭建
安装 Jest
npm i jest@27.0.2
npm i babel-jest@27.0.2// babel进行转码
npm i ts-jest@27.0.2// ts语法解析
npm i @types/jest// ts语法定义
说明
项目中如果使用的 TypeScript,需要安装 ts-jest、@types/jest,并在 tsconfig.json 文件中加入 "types": ["jest"]
注意:jest、ts-jest、babel-jest 三个依赖包的版本需要一致,否则可能会报错。
安装 testing-library
npm i @testing-library/jest-dom @testing-library/react-native @testing-library/react-hooks -D
说明
·@testing-library/jest-dom提供了一组可用于扩展 jest 的自定义 jest 匹配器。这些将使您的测试更具声明性,更易于阅读维护。
· @testing-library/react-native用于测试 React Native 组件。(如果测试React,请选择@testing-library/react)
· @testing-library/react-hooks为React Hook创建一个简单的测试工具,并在函数组件体内运行。可选择性下载该项。
package.json 文件配置
"scripts": {
...
"test": "jest test --verbose -u --watch",
}
"jest": {
"preset": "react-native"
}
babel 文件配置
·如果项目中 bebel 配置使用的是 .babelrc.js,需要将文件转为 babel.config.js(可能会影响项目原本编译结果)
·如果转化为 babel.config.js 文件影响项目编译结果,可以保留 .babelrc.js 文件,然后新建 babel.config.js 文件并将 presets 声明移入 babel.config.js
示例:
// babel.config.js
module.exports = (api) => {
api.cache.never();
return {
"presets": process.env.MINI_PROGRAM === 'true' ? [] : ['module:metro-react-native-babel-preset'],
}
};
jest.config.js 文件配置
项目根目录新建 jest.config.js,配置可参考如下(笔者项目使用的是 ts 的语法,js 等配置可自行添加)。
const { defaults } = require('ts-jest/presets');
module.exports = {
...defaults,
preset: 'react-native',
globals: {
'ts-jest': {
babelConfig: true,
},
},
// 定义测试的文件目录,与配置文件同级目录下的 tests 文件夹内的 .tsx/.jsx 后缀名文件
testRegex: '(/tests/.*\\.(test|spec))\\.[tj]sx?$',
// 定义文件的编译方式
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
// 定义了忽略进行 jest 执行的依赖包
transformIgnorePatterns: [
'node_modules/(?!(react-native|@testing-library|react-navigation|@react-navigation/.*|@react-native-community)/)',
],
testPathIgnorePatterns: ['<rootDir>/node_modules/', '\\.snap$'],
// 缓存文件生成的目录地址,注意在.gitignore 中忽略
cacheDirectory: '.jest/cache',
testEnvironment: 'jsdom',
moduleNameMapper: {
'^[@./a-zA-Z0-9$_-]+\\.(png|gif)$': '<rootDir>/node_modules/react-native/Libraries/Image/RelativeImageStub',
},
};
基础知识
简单例子
常见的一个测试文件如下:
// sum.test.ts
const sum = (a, b) => {
return a + b;
}
describe('Unit test explain', () => {
test('sum success', () => {
expect(sum(1, 2)).toBe(3);
})
})
·describe 通常将测试套件分解为组件。意思就是我们可以将某个组件的整体功能进行分解,按每个模块或者每个功能进行区分。而 test/it 是执行个别测试的地方,用来描述每一个测试功能点。
· test 称为测试用例,接收两个参数。第一个参数是该用例的描述,第二个是测试函数,用来定义逻辑。与 it 同义。
· expect 是期望的意思,整行称为断言。上面的意思就是期望 1 + 2 能否等于 3。
· toBe 是一个匹配器,匹配 expect 中预期的值和匹配器中的值是否相等。
常见的匹配器
· toBe(value) 是否完全相等,等同于 ===
· toBeNull(value) 是否为 null
· toBeUndefined() 是否为 undefined
· toBeNaN() 匹配 NaN
· toBeTruthy() 匹配 true
· toBeFalsy()匹配 false
· .not 后续匹配取反
· toMatch(regexpOrString) 检查字符串是否匹配,可传字符串或者正则表达式
· toMatchObject(object) 判断一个对象/数组是否属于子集
· toContain(item) 匹配数组/Set/字符串中是否包含 item
· **toContainEqual(item) 匹配数组中是否包含一个特定对象
· toHaveProperty(keyPath, value) 匹配对象中深度嵌套的属性,判断在指定 keyPath 下是否有 value 属性
· toHaveLength(number) 匹配对象 length 值
· toThrow(err)/toThrowError(err) 匹配异常
· expect 中传入函数才可以匹配到异常
· toBeCalled()/toHaveBeCalled() 匹配函数是否被执行
· toReturn()/toHaveReturned() 匹配函数是否有返回值
· toReturnWith(value)/toHaveReturnedWith(value) 匹配函数返回值是否匹配
Testing Library
我们再来看一个例子:
import React from 'react';
import "setimmediate";
import { render } from '@testing-library/react-native';
import Button from '../lib/button';
describe('Button unit test', () => {
test('render success', () => {
const { getByTestId } = render(<Button testID='button' />);
// 期待找到 testID 为 button 的元素
expect(() => getByTestId('button')).not.toThrow(/Unable to find an element with testID/);
})
})
上述例子是为了测试 Button 组件是否渲染成功,并通过 testID 标识是否能找到相应的 Dom 元素。
·render() 用来对组件的渲染,透出的 getByTestId 方法用以接下来对 testID 的查找
· getByTestId() 查找渲染的元素中,有无 testID 为 btn 的元素
· toThrow(err)匹配器须传入函数才可以匹配到异常,所以我们传入() => getByTestId('btn') 并且预期不抛出无法根据 testID 找到节点的错误
常见查询器
先看概述:
· getBy... 返回查询的匹配节点,如果未查找到、或者查找到多个,则抛出一个描述性的错误。
· quertBy... 返回查询的匹配节点,如果没有匹配的元素则返回null。如果匹配到多个,则抛出错误。
· findBy... 返回一个Promise,该Promise在找到与匹配项时进行解析。如果未匹配到元素,或者超过匹配时间(默认1000ms)时,Promise将被拒绝。
同上,getAllBy... quertAllBy... findAllBy... 查询匹配条件的全部节点。
常用的查询器:
· getByRole 查询具有给定角色的元素。
· getByLabelText 查询label与给定文本匹配的元素。
· getByPlaceholderText 查询所有匹配占位符文本的元素。
· getByText 查询所有文本节点与给定文本匹配的元素。
· getByTestId 查询元素包含data-testid="${yourId}"的元素,在RN组件中,默认提供了testID属性。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理