这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战
概念
Jest: Jest是一套测试框架,被Facebook用来测试包括React应用在内的所有JavaScript代码。Jest的一个理念是提供一套完整集成的 “零配置” 测试体验。- 官方推荐
- 支持输出详细的代码覆盖率报告
- 强大的
mock函数 - 灵活方便引入第三方库
react-testing-library: 一个DOM测试库,官方推荐的Jest的第三方库,类似于jQuery,提供更简洁的Api,如定位元素,断言等- 思想:区别于enzyme,
react-testing-library主要从用户角度去写单元测试,比如一个“详情”按钮,功能是点击按钮有弹窗,且发送请求,然后显示页面详细信息;那么我测试的思路点可以是,页面有一个按钮、文字为“详情”、点击后,页面会有个弹窗、弹窗内容显示“...”;而不用去关心组件内部代码的实现细节,比如props传值等等 - 单元测试编写思路,参考ARRANGE-ACT-ASSERT模式
- Arrange:准备好需要测试的内容,比如
render出组件对应的dom元素;比如初始化一些变量值 - Act:包含组件的功能,可能是用户的交互如
userEvent:click;或者发送异步请求使用async...await;或者调用某些方法; - Assert:断言
expect预期的结果。Act后会导致某些预期的反应,比如click后页面元素的变化;异步请求后,数据的变化;方法的调用次数等等;
- Arrange:准备好需要测试的内容,比如
- 思想:区别于enzyme,
使用环境
- 项目如果是使用Create-React-App创建的,则默认已安装
@testing-library/jest-dom: "^5.14.1"@testing-library/react: "^11.2.7"@testing-library/user-event: "^12.8.3"- 根据项目具体情况可额外添加其它依赖,如:
@testing-library/react-hooks
- 可在
package.json中添加jest(与script同级),或者在项目中添加jest.config.ts文件"jest": { "verbose": true, // 是否输出 descripe 和 test/it 中的测试描述信息 "collectCoverage": true, // 是否输出测代码试覆盖率 "setupFilesAfterEnv": [ "<rootDir>/src/setupTests.ts" // 在每个测试文件执行之前调用一些默认执行代码的文件路径 ] },- 下面为
verbose属性配置为false、true后,输出信息的区别 - 下面为
setupTests.ts文件中可配置默认执行代码例子
import React from 'react' import '@testing-library/jest-dom'; import { renderHook } from "@testing-library/react-hooks"; beforeEach(() => { return renderHook(() => { ... }) }) ... - 下面为
知识点:react-testing-library
- DOM API
- render:
Example:function render( ui: React.ReactElement<any>, options?: {通常比较少用到,包含container、baseElement等可配置选项} ):RenderResult // RenderResult返回一些元素定位方法:getByText、queryAllByTestId等 eg:const {getByText, queryAllByTestId} = render(<Component />) // 也可以直接调用render,引入screen调用// my-component.test.ts import {render} from '@testing-library/react' import userEvent from '@testing-library/user-event' import '@testing-library/jest-dom' import MyComponent from './my-component' // 使用render方法渲染组件或者dom,使用定位方法如 getByText 定位元素,然后断言 test('test Hello World', ()=> { const {container, getByText} = render(<MyComponent/>) // Arrange expect(getByText('Hello World').toBeInTheDocument() }) - cleanup:
- Unmounts React trees that were mounted with render
- 防止内存泄漏 Example:
import {render, cleanup} from '@testing-library/react' // 结合钩子函数使用,在每次调用测试前清楚之前渲染的dom afterEach(cleanup) dom定位方法-
getBy:定位页面已存在dom元素,如果不存在则抛出异常
-getByText: find by element text content
-getByRole: find by aria role
-getByLabelText: find by label or aria-label text content
-getByPlaceholderText: find by input placeholder value
-getByAltText: find by img alt attribute
-getByTitle: find by title attribute or svg title tag
-getByDisplayValue: find by form element current value
-getByTestId: find by data-testid attribute -
queryBy: 定位页面不存在的dom元素,如果不存在,返回null,不抛出异常(具体方法同上)
-
fintBy: 定位页面当中的异步元素,如果不存在,抛出异常(具体方法同上)
-
下面可以看到三大类方法区别:
-
- render:
- 异步方法结合
async...await使用- waitFor(Promise):会一直等到内部函数执行完毕或者抛出异常为止
- waitForElementToBeRemoved(Promise):直到内部函数不返回Dom元素为止
- userEvent
click(element):单击dbClick:双击async type(element, text, [options]):输入文本selectOptions(element, values):表单选择tab({shift, focusTrap}):模拟 tab 键(切换 focus)
- 补充Jest快照测试
snapshotimport React from 'react'; import renderer from 'react-test-renderer'; import Link from '../Link.react'; it('renders correctly', () => { const tree = renderer .create(<Link page="http://www.facebook.com">Facebook</Link>) .toJSON() expect(tree).toMatchSnapshot() })
总结
学习单元测试,结合ARRANGE-ACT-ASSERT这个思路,根据官方文档掌握对应每一步要用的api,按照用户的角度去写单元测试就很简单了,通常就是以下三步:
- Arrange:元素定位结合
render,jest-dom——getby/queryby/findby...,jest.mock()...等等 - Act:
userEvent,waitFor,async...await...等等 - Assert: 结合jest-dom-
toBeInTheDocument,toBeChecked等等