0


【实战】二、Jest难点进阶(三) —— 前端要学的测试课 从Jest入门到TDD BDD双实战(七)

文章目录


学习内容来源: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.0babel-jest^27.4.2babel-loader^8.2.3babel-plugin-named-asset-import^0.3.8babel-preset-react-app^10.0.1bfj^7.0.2browserslist^4.18.1camelcase^6.2.1case-sensitive-paths-webpack-plugin^2.4.0css-loader^6.5.1css-minimizer-webpack-plugin^3.2.0dotenv^10.0.0dotenv-expand^5.1.0eslint^8.3.0eslint-config-react-app^7.0.1eslint-webpack-plugin^3.1.1file-loader^6.2.0fs-extra^10.0.0html-webpack-plugin^5.5.0identity-obj-proxy^3.0.0jest^27.4.3jest-enzyme^7.1.2jest-resolve^27.4.2jest-watch-typeahead^1.0.0mini-css-extract-plugin^2.4.5postcss^8.4.4postcss-flexbugs-fixes^5.0.2postcss-loader^6.2.1postcss-normalize^10.0.1postcss-preset-env^7.0.1prompts^2.4.2react^18.2.0react-app-polyfill^3.0.0react-dev-utils^12.0.1react-dom^18.2.0react-refresh^0.11.0resolve^1.20.0resolve-url-loader^4.0.0sass-loader^12.3.0semver^7.3.5source-map-loader^3.0.0style-loader^3.3.1tailwindcss^3.0.2terser-webpack-plugin^5.2.5web-vitals^2.1.4webpack^5.64.4webpack-dev-server^4.6.0webpack-manifest-plugin^4.0.2workbox-webpack-plugin^6.4.1"
具体配置、操作和内容会有差异,“坑”也会有所不同。。。


一、Jest 前端自动化测试框架基础入门

  • 一、Jest 前端自动化测试框架基础入门(一)

  • 一、Jest 前端自动化测试框架基础入门(二)

  • 一、Jest 前端自动化测试框架基础入门(三)

  • 一、Jest 前端自动化测试框架基础入门(四)

二、Jest难点进阶

  • 二、Jest难点进阶(一)
  • 二、Jest难点进阶(二)

3.mock timers

接下来学习一下对定时器的模拟

新建 Jest\src\lesson11\index.js

exportdefault(cbk)=>{setTimeout(()=>{cbk()},3000)}

新建 Jest\src\lesson11_tests_\index.test.js

import timer from"../index";test('测试 timer',()=>{timer(()=>{expect(2).toEqual(1)})})

执行测试用例,竟然成功了。。由于timer是一个异步函数,jest不会等cbk函数执行完毕,在cbk挂起期间没有明显问题直接就会返回成功

给它加点料,编辑 Jest\src\lesson11_tests_\index.test.js

import timer from"../index";test('测试 timer',(done)=>{timer(()=>{expect(2).toEqual(1)done()})})

执行测试用例报错,这才对嘛(改为

expect(2).toEqual(1)

后会成功)

使用了

done()

,这时候测试用例就会等

done()

执行完毕出结果,但是若时间设置较长,这样的等待显然是不合理的

接下来进入正题,模拟 timer

编辑 Jest\src\lesson11_tests_\index.test.js(模拟

timer

后换一种测试方式)

import timer from"../index";

jest.useFakeTimers()test('测试 timer',()=>{const fn = jest.fn();timer(fn);expect(fn).toHaveBeenCalledTimes(1)})

使用

jest.useFakeTimers()

模拟 timer

执行测试用例报错,信息如下(确实模拟了

timer

,但是没有执行)

Expected number ofcalls:1
Received number ofcalls:0

编辑 Jest\src\lesson11_tests_\index.test.js(执行模拟的

timer

import timer from"../index";

jest.useFakeTimers()test('测试 timer',()=>{const fn = jest.fn();timer(fn);
  jest.runAllTimers()expect(fn).toHaveBeenCalledTimes(1)})

执行测试用例成功!

接下来看看其他相关用法

编辑 Jest\src\lesson11\index.js(定时器里面再放入一层,并执行

cbk

函数)

exportdefault(cbk)=>{setTimeout(()=>{cbk()setTimeout(()=>{cbk()},3000)},3000)}

这时再运行之前的测试用例就通不过了,因为

runAllTimers

后,

cbk

执行了两次

如何测试时只运行当前已触发的定时器呢?(运行代码时,只有最外层定时器加入队列,即触发)

编辑 Jest\src\lesson11_tests_\index.test.js(

runAllTimers

改为

runOnlyPendingTimers

import timer from"../index";

jest.useFakeTimers()test('测试 timer',()=>{const fn = jest.fn();timer(fn);
  jest.runOnlyPendingTimers()expect(fn).toHaveBeenCalledTimes(1)})

执行测试用例成功!

很显然只有这两个在日常测试是不够用的,接下来尝试另一个函数

编辑 Jest\src\lesson11_tests_\index.test.js(

runOnlyPendingTimers

改为

advanceTimersByTime

import timer from"../index";

jest.useFakeTimers()test('测试 timer',()=>{const fn = jest.fn();timer(fn);
  jest.advanceTimersByTime(3000)expect(fn).toHaveBeenCalledTimes(1)})

执行测试用例成功!

advanceTimersByTime

相当于是时间快进器,测试用例中在 3000 这个节点 fn 执行第一次,在 6000 这个节点 fn 执行第二次,因此在另外几个时间段的执行结果便呼之欲出了

当然

advanceTimersByTime

可以使用多次,不过需要注意的是,下一次使用是在上一次”快进“的基础上再次”快进“的

编辑 Jest\src\lesson11_tests_\index.test.js(多次使用

advanceTimersByTime

import timer from"../index";

jest.useFakeTimers()test('测试 timer',()=>{const fn = jest.fn();timer(fn);
  jest.advanceTimersByTime(3000)expect(fn).toHaveBeenCalledTimes(1)
  jest.advanceTimersByTime(3000)expect(fn).toHaveBeenCalledTimes(2)})

执行测试用例成功!

但是多个测试用例之间 advanceTimersByTime 会有相互影响

编辑 Jest\src\lesson11\index.js(在之前定时器里面再放入一层的基础上,再放入一层,并再执行

cbk

函数,一共三层,最终执行三次)

exportdefault(cbk)=>{setTimeout(()=>{cbk()setTimeout(()=>{cbk()setTimeout(()=>{cbk()},3000)},3000)},3000)}

编辑 Jest\src\lesson11_tests_\index.test.js(在多个测试用例中使用

advanceTimersByTime

import timer from"../index";

jest.useFakeTimers()describe('测试 timer',()=>{const fn = jest.fn();timer(fn);test('第一次测试 timer',()=>{
    jest.advanceTimersByTime(3000)expect(fn).toHaveBeenCalledTimes(1)})test('第二次测试 timer',()=>{
    jest.advanceTimersByTime(3000)expect(fn).toHaveBeenCalledTimes(1)})test('第三次测试 timer',()=>{
    jest.advanceTimersByTime(3000)expect(fn).toHaveBeenCalledTimes(1)})})

执行测试用例成功!(03 36 6~9 各执行一次)

调整参数:

import timer from"../index";

jest.useFakeTimers()describe('测试 timer',()=>{const fn = jest.fn();timer(fn);test('第一次测试 timer',()=>{
    jest.advanceTimersByTime(2000)expect(fn).toHaveBeenCalledTimes(0)})test('第二次测试 timer',()=>{
    jest.advanceTimersByTime(4000)expect(fn).toHaveBeenCalledTimes(2)})test('第三次测试 timer',()=>{
    jest.advanceTimersByTime(3000)expect(fn).toHaveBeenCalledTimes(1)})})

执行测试用例成功!(02 没有执行 26 执行两次 6~9 执行一次)

调整参数:

import timer from"../index";

jest.useFakeTimers()describe('测试 timer',()=>{const fn = jest.fn();timer(fn);test('第一次测试 timer',()=>{
    jest.advanceTimersByTime(2000)expect(fn).toHaveBeenCalledTimes(0)})test('第二次测试 timer',()=>{
    jest.advanceTimersByTime(2000)expect(fn).toHaveBeenCalledTimes(1)})test('第三次测试 timer',()=>{
    jest.advanceTimersByTime(5000)expect(fn).toHaveBeenCalledTimes(2)})})

执行测试用例成功!(01 没有执行 12 没有执行 2~9 执行三次)

从这三次测试调整中可以发现,toHaveBeenCalledTimes 统计的是每个测试用例里的 fn 调用次数,而 advanceTimersByTime 之间从前往后是相互叠加的

若是想要隔离这种影响,可以使用钩子函数

编辑 Jest\src\lesson11_tests_\index.test.js

import timer from"../index";beforeEach(()=>{
  jest.useFakeTimers();})describe('测试 timer',()=>{test('第一次测试 timer',()=>{const fn = jest.fn();timer(fn);
    jest.advanceTimersByTime(2000)expect(fn).toHaveBeenCalledTimes(0)})test('第二次测试 timer',()=>{const fn = jest.fn();timer(fn);
    jest.advanceTimersByTime(2000)expect(fn).toHaveBeenCalledTimes(1)})test('第三次测试 timer',()=>{const fn = jest.fn();timer(fn);
    jest.advanceTimersByTime(5000)expect(fn).toHaveBeenCalledTimes(2)})})

执行测试用例,只有第一个成功!成功隔离(这里虚晃一枪,请看到最后)

注意使用了钩子函数之后,只有在测试用例中调用的定时器才是经过mock的!

来个烧脑的,编辑 Jest\src\lesson11_tests_\index.test.js(将

fn

的定义放在外边)

import timer from"../index";beforeEach(()=>{
  jest.useFakeTimers();})describe('测试 timer',()=>{const fn = jest.fn();test('第一次测试 timer',()=>{timer(fn);
    jest.advanceTimersByTime(2000)expect(fn).toHaveBeenCalledTimes(0)})test('第二次测试 timer',()=>{timer(fn);
    jest.advanceTimersByTime(2000)expect(fn).toHaveBeenCalledTimes(1)})test('第三次测试 timer',()=>{timer(fn);
    jest.advanceTimersByTime(5000)expect(fn).toHaveBeenCalledTimes(2)})})

执行测试用例,前两个成功!最后一个:

Expected number ofcalls:2
Received number ofcalls:5

执行过程:


0~2
第一个函数执行,第一个定时器触发但没有执行完 0

2~4
第一个函数执行,第一个定时器执行完,第二个定时器触发但没有执行完 1
第二个函数执行,,第一个定时器触发但没有执行完 0

4~9
第一个函数执行,第二个和第三个定时器执行完 2
第二个函数执行,第一个和第二个定时器执行完 2
第三个函数执行,第一个定时器执行完 1

并没有视频课程中所说的隔离。。。但是功能理解了,over


本文仅作记录, 实战要点待后续专文总结,敬请期待。。。

标签: 前端 tdd

本文转载自: https://blog.csdn.net/qq_32682301/article/details/136156985
版权归原作者 程序边界 所有, 如有侵权,请联系我们删除。

“【实战】二、Jest难点进阶(三) —— 前端要学的测试课 从Jest入门到TDD BDD双实战(七)”的评论:

还没有评论