【实战】一、Jest 前端自动化测试框架基础入门(三) —— 前端要学的测试课 从Jest入门到TDD BDD双实战(三)
后台-插件-广告管理-内容页头部广告(手机) |
文章目录
- 一、Jest 前端自动化测试框架基础入门
- 7.异步代码的测试方法
- 8.Jest 中的钩子函数
- 9.钩子函数的作用域
学习内容来源:Jest入门到TDD/BDD双实战_前端要学的测试课
相对原教程,我在学习开始时(2023.08)采用的是当前最新版本:
项 | 版本 |
---|---|
@babel/core | ^7.16.0 |
@pmmmwh/react-refresh-webpack-plugin | ^0.5.3 |
@svgr/webpack | ^5.5.0 |
@testing-library/jest-dom | ^5.17.0 |
@testing-library/react | ^13.4.0 |
@testing-library/user-event | ^13.5.0 |
babel-jest | ^27.4.2 |
babel-loader | ^8.2.3 |
babel-plugin-named-asset-import | ^0.3.8 |
babel-preset-react-app | ^10.0.1 |
bfj | ^7.0.2 |
browserslist | ^4.18.1 |
camelcase | ^6.2.1 |
case-sensitive-paths-webpack-plugin | ^2.4.0 |
css-loader | ^6.5.1 |
css-minimizer-webpack-plugin | ^3.2.0 |
dotenv | ^10.0.0 |
dotenv-expand | ^5.1.0 |
eslint | ^8.3.0 |
eslint-config-react-app | ^7.0.1 |
eslint-webpack-plugin | ^3.1.1 |
file-loader | ^6.2.0 |
fs-extra | ^10.0.0 |
html-webpack-plugin | ^5.5.0 |
identity-obj-proxy | ^3.0.0 |
jest | ^27.4.3 |
jest-enzyme | ^7.1.2 |
jest-resolve | ^27.4.2 |
jest-watch-typeahead | ^1.0.0 |
mini-css-extract-plugin | ^2.4.5 |
postcss | ^8.4.4 |
postcss-flexbugs-fixes | ^5.0.2 |
postcss-loader | ^6.2.1 |
postcss-normalize | ^10.0.1 |
postcss-preset-env | ^7.0.1 |
prompts | ^2.4.2 |
react | ^18.2.0 |
react-app-polyfill | ^3.0.0 |
react-dev-utils | ^12.0.1 |
react-dom | ^18.2.0 |
react-refresh | ^0.11.0 |
resolve | ^1.20.0 |
resolve-url-loader | ^4.0.0 |
sass-loader | ^12.3.0 |
semver | ^7.3.5 |
source-map-loader | ^3.0.0 |
style-loader | ^3.3.1 |
tailwindcss | ^3.0.2 |
terser-webpack-plugin | ^5.2.5 |
web-vitals | ^2.1.4 |
webpack | ^5.64.4 |
webpack-dev-server | ^4.6.0 |
webpack-manifest-plugin | ^4.0.2 |
workbox-webpack-plugin | ^6.4.1" |
具体配置、操作和内容会有差异,“坑”也会有所不同。。。
一、Jest 前端自动化测试框架基础入门
- 一、Jest 前端自动化测试框架基础入门(一)
- 一、Jest 前端自动化测试框架基础入门(二)
7.异步代码的测试方法
安装 axios
npm i axios@0.19.0 -S- 1
新建 fetchData.js:
import axios from 'axios' export const fetchData = (fn) => { axios.get('http://www.dell-lee.com/react/api/demo.json').then(res => fn(res.data)) }- 1
- 2
- 3
- 4
- 5
新建单元测试文件 fetchData.test.js:
import fetchData from './fetchData' // 回调类型异步函数的测试 test('fetchData 返回结果为 { success: true }', (done) => { fetchData((data) => { expect(data).toEqual({ success: true }) // 只有当 done 函数被执行到才认为是测试用例执行结束 done(); }) })- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
不使用 done 的话,测试用例执行到 fetchData 之后直接就返回 pass
还有一种情况,将 Promise 对象直接返回出来:修改 fetchData.js:
import axios from 'axios' export const fetchData = () => { return axios.get('http://www.dell-lee.com/react/api/demo.json') }- 1
- 2
- 3
- 4
- 5
相应修改单元测试文件 fetchData.test.js:
import fetchData from './fetchData' test('fetchData 返回结果为 Promise: { success: true }', () => { return fetchData().then((res) => { expect(res.data).toEqual({ success: true }) }) })- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
若是想要单独测试 404,可以修改为如下
import fetchData from './fetchData' test('fetchData 返回结果为 404', () => { expect.assertions(1) // 下面的 expect 至少执行一个 return fetchData().catch((e) => { expect(e.toString().indexOf('404') > -1).toBe(true) }) })- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
若是不使用 expect.assertions ,当测试 接口访问成功,没走 catch 时,相当于啥也没有执行,也会通过,加上后若是接口访问成功会报错:Expected one assertion to be called but received zero assertion calls.
还有可以使用 expect 自带的函数识别结果:
test('fetchData 返回结果为 Promise: { success: true }', () => { return expect(fetchData()).resolves.toMatchObject({ data: { success: true } }) })- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
除了使用 return 还可以使用 async…await 的语法:
test('fetchData 返回结果为 Promise: { success: true }', async () => { await expect(fetchData()).resolves.toMatchObject({ data: { success: true } }) })- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
还可以使用 async…await 先拿到响应结果,再判断:
test('fetchData 返回结果为 Promise: { success: true }', async () => { const res = await fetchData() expect(res.data).toEqual({ success: true }) })- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
8.Jest 中的钩子函数
Jest 中的钩子函数指的是在 Jest 执行过程中到某一特定时刻被自动调用的函数,类似 Vue/React 中的生命周期函数
新建 Counter.js
export default class Counter { constructor() { this.number = 0 } addOne() { this.number += 1 } minusOne() { this.number -= 1 } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
新建 Counter.test.js
import Counter from "./Counter"; describe('测试 Counter', () => { const counter = new Counter(); test('测试 addOne 方法', () => { counter.addOne() expect(counter.number).toBe(1) }) test('测试 minusOne 方法', () => { counter.minusOne() expect(counter.number).toBe(0) }) })- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
运行测试用例,直接通过,但是两个测试用例共用了一个实例 counter,相互之间有影响,这显然是不可以的,可以引入 Jest 的 钩子函数来做预处理
修改 Counter.test.js
import Counter from "./Counter"; describe('测试 Counter', () => { let counter = null beforeAll(() => { console.log('beforeAll') }) beforeEach(() => { console.log('beforeEach') counter = new Counter(); }) afterEach(() => { console.log('afterEach') // counter = null }) afterAll(() => { console.log('afterAll') }) test('测试 addOne 方法', () => { console.log('测试 addOne ') counter.addOne() expect(counter.number).toBe(1) }) test('测试 minusOne 方法', () => { console.log('测试 minusOne ') counter.minusOne() expect(counter.number).toBe(-1) }) })- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
这样就不会相互之间产生影响了
编辑 Counter.js 新增两个方法
export default class Counter { constructor() { this.number = 0 } addOne() { this.number += 1 } addTwo() { this.number += 2 } minusOne() { this.number -= 1 } minusTwo() { this.number -= 2 } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
这时候测试文件怎么写呢?很显然功能有分类,可以使用 describe
编辑 Counter.test.js
import Counter from "./Counter"; describe('测试 Counter', () => { let counter = null beforeAll(() => { console.log('beforeAll') }) beforeEach(() => { console.log('beforeEach') counter = new Counter(); }) afterEach(() => { console.log('afterEach') // counter = null }) afterAll(() => { console.log('afterAll') }) describe('测试“增加”相关的方法', () => { test('测试 addOne 方法', () => { console.log('测试 addOne ') counter.addOne() expect(counter.number).toBe(1) }) test('测试 addTwo 方法', () => { console.log('测试 addTwo ') counter.addTwo() expect(counter.number).toBe(2) }) }) describe('测试“减少”相关的方法', () => { test('测试 minusOne 方法', () => { console.log('测试 minusOne ') counter.minusOne() expect(counter.number).toBe(-1) }) test('测试 minusTwo 方法', () => { console.log('测试 minusTwo ') counter.minusTwo() expect(counter.number).toBe(-2) }) }) })- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
测试日志如下:
测试 Counter 测试“增加”相关的方法 √ 测试 addOne 方法 (6ms) √ 测试 addTwo 方法 (4ms) 测试“减少”相关的方法 √ 测试 minusOne 方法 (4ms) √ 测试 minusTwo 方法 (4ms) console.log Counter.test.js:8 beforeAll console.log Counter.test.js:12 beforeEach console.log Counter.test.js:27 测试 addOne console.log Counter.test.js:17 afterEach console.log Counter.test.js:12 beforeEach console.log Counter.test.js:32 测试 addTwo console.log Counter.test.js:17 afterEach console.log Counter.test.js:12 beforeEach console.log Counter.test.js:41 测试 minusOne console.log Counter.test.js:17 afterEach console.log Counter.test.js:12 beforeEach console.log Counter.test.js:46 测试 minusTwo console.log Counter.test.js:17 afterEach console.log Counter.test.js:22 afterAll Test Suites: 1 passed, 1 total Tests: 4 passed, 4 total Snapshots: 0 total Time: 4.411s- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
9.钩子函数的作用域
每一个 describe 都可以有自己的 beforeAll、afterAll、beforeEach、afterEach,执行顺序是从外往内。
外部的钩子函数可以对当前 describe 所有的测试用例起作用,而内部的只对内部的测试用例起作用,这就是钩子函数的作用域。
可以自行编写尝试,这里就不再赘述了。
还有一个单元测试小技巧,test 使用 only 修饰符可以让单元测试只运行这一个测试用例:
test.only('', () => {})- 1
注意,代码执行顺序中,最先执行的是不包含在任何测试用例和钩子函数中的语句(直接暴露在各个 describe 内部最外层的语句),且只执行一次,后续才是测试用例和钩子函数的执行。
本文仅作记录, 实战要点待后续专文总结,敬请期待。。。
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。
在线投稿:投稿 站长QQ:1888636
后台-插件-广告管理-内容页尾部广告(手机) |