组件测试
为什么要进行测试
- 测试可以确保得到预期的结果
- 作为现有代码行为的描述
- 促使开发者写可测试的代码,一般可测试的代码可读性也会高一点
- 如果依赖的组件有修改,受影响的组件能在测试中发现错误
测试类型
- 单元测试:指的是以原件的单元为单位,对软件进行测试。单元可以是一个函数,也可以是一个模块或一个组件,基本特征就是只要输入不变,必定返回同样的输出。一个软件越容易些单元测试,就表明它的模块化结构越好,给模块之间的耦合越弱。React的组件化和函数式编程,天生适合进行单元测试
- 功能测试:相当于是黑盒测试,测试者不了解程序的内部情况,不需要具备编程语言的专门知识,只知道程序的输入、输出和功能,从用户的角度针对软件界面、功能和外部结构进行测试,不考虑内部的逻辑
- 集成测试:在单元测试的基础上,将所有模块按照设计要求组装成子系统或者系统,进行测试
- 冒烟测试:在正式全面的测试之前,对主要功能进行的与测试,确认主要功能是否满足需要,软件是否能正常运行
Jest
Jest是Facebook开源的一个前端测试框架,主要用于React和React Native的单元测试,已被集成在create-react-app中。Jest特点:
- 易用性:基于Jasmine,提供断言库,支持多种测试风格
- 适应性:Jest是模块化、可扩展和可配置的
- 沙箱和快照:Jest内置了JSDOM,能够模拟浏览器环境,并且并行执行
- 快照测试:Jest能够对React组件树进行序列化,生成对应的字符串快照,通过比较字符串提供高性能的UI检测
- Mock系统:Jest实现了一个强大的Mock系统,支持自动和手动mock
- 支持异步代码测试:支持Promise和async/await
- 自动生成静态分析结果:内置Istanbul,测试代码覆盖率,并生成对应的报告
globals API
- describe(name, fn):描述块,讲一组功能相关的测试用例组合在一起
- it(name, fn, timeout):别名test,用来放测试用例
- afterAll(fn, timeout):所有测试用例跑完以后执行的方法
- beforeAll(fn, timeout):所有测试用例执行之前执行的方法
- afterEach(fn):在每个测试用例执行完后执行的方法
- beforeEach(fn):在每个测试用例执行之前需要执行的方法
全局和describe都可以有上面四个周期函数,describe的after函数优先级要高于全局的after函数,describe的before函数优先级要低于全局的before函数
脚手架内置了通用单元测试框架Jest
项目测试时直接运行下面的指令
"test": "react-scripts test",
项目启动测试
Jest will look for test files with any of the following popular naming conventions:
- Files with .js suffix in tests folders.
- Files with .test.js suffix.
- Files with .spec.js suffix.
如果是Ts会包含ts结尾的文件
单独执行一个.test文件
npx jest jest.test.js
React的特殊测试工具
- ReactTestUtils 是官方提供的测试api,比较复杂难用
- Airbnb 发布了一款叫作 Enzyme 的测试工具,通过它能够轻松对 React 组件的输出进行断言、操控和遍历。类似jQuery的获取方式
- 官方推荐使用 React Testing Library,它使得针对组件编写测试用例就像终端用户在使用它一样方便。
React Testing Library
React Testing Library是一种简单轻量的测试工具,最新的脚手架已经内置了
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
jest-dom
详细的文档可以去https://testing-library.com/docs/react-testing-library/api查看。
注意jest-dom是一个jest对于React的特殊断言库
- Custom matchers
- toBeDisabled
- toBeEnabled
- toBeEmpty
- toBeEmptyDOMElement
- toBeInTheDocument
- toBeInvalid
- toBeRequired
- toBeValid
- toBeVisible
- toContainElement
- toContainHTML
- toHaveAttribute
- toHaveClass
- toHaveFocus
- toHaveFormValues
- toHaveStyle
- toHaveTextContent
- toHaveValue
- toHaveDisplayValue
- toBeChecked
- toBePartiallyChecked
- toHaveDescription
- Deprecated matchers
- toBeInTheDOM
官方创建了一个setupTests.ts文件在src下,导入了这个库
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';
user-event
user-event是一个测试库的配套库,它提供了比内置的fireEvent方法更高级的浏览器交互模拟。主要用来触发一些事件
it("do active",()=>{
expect(menuElement).toBeInTheDocument();
let noActive = wrapper.getByText("no active");
expect(noActive).toBeInTheDocument();
expect(noActive).not.toHaveClass('menu-item is-active')
// 触发点击事件
fireEvent.click(noActive)
expect(noActive).toHaveClass('menu-item is-active')
expect(activeElement).not.toHaveClass('menu-item is-active')
})
异步断言 - Waiting for appearance
如果您需要等待一个元素出现,那么async wait实用程序允许您等待断言得到满足后再继续。等待实用程序会重试,直到查询通过或超时。
import { wait } from "@testing-library/react";
fireEvent.mouseEnter(dropdownElement)
await wait(() => {
expect(wrapper.queryByText('111')).toBeVisible()
})