【实战】二、Jest难点进阶(三) —— 前端要学的测试课 从Jest入门到TDD BDD双实战(七)
文章目录
- 一、Jest 前端自动化测试框架基础入门
- 二、Jest难点进阶
- 3.mock timers
学习内容来源: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 前端自动化测试框架基础入门(二)
- 一、Jest 前端自动化测试框架基础入门(三)
- 一、Jest 前端自动化测试框架基础入门(四)
二、Jest难点进阶
- 二、Jest难点进阶(一)
- 二、Jest难点进阶(二)
3.mock timers
接下来学习一下对定时器的模拟
新建 Jest\src\lesson11\index.js
export default (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 of calls: 1
Received number of calls: 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 函数)
export default (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 函数,一共三层,最终执行三次)
export default (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)})
})
执行测试用例成功!(0~3 3~6 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)})
})
执行测试用例成功!(0~2 没有执行 2~6 执行两次 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)})
})
执行测试用例成功!(0~1 没有执行 1~2 没有执行 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 of calls: 2
Received number of calls: 5
执行过程:
0~2
第一个函数执行,第一个定时器触发但没有执行完 02~4
第一个函数执行,第一个定时器执行完,第二个定时器触发但没有执行完 1
第二个函数执行,,第一个定时器触发但没有执行完 04~9
第一个函数执行,第二个和第三个定时器执行完 2
第二个函数执行,第一个和第二个定时器执行完 2
第三个函数执行,第一个定时器执行完 1
并没有视频课程中所说的隔离。。。但是功能理解了,over
本文仅作记录, 实战要点待后续专文总结,敬请期待。。。
相关文章:
【实战】二、Jest难点进阶(三) —— 前端要学的测试课 从Jest入门到TDD BDD双实战(七)
文章目录 一、Jest 前端自动化测试框架基础入门二、Jest难点进阶3.mock timers 学习内容来源:Jest入门到TDD/BDD双实战_前端要学的测试课 相对原教程,我在学习开始时(2023.08)采用的是当前最新版本: 项版本babel/core…...
Python编程中的异常处理
什么是异常? 程序错误(errors)有时也被称为程序异常(exceptions),这是每个编程人员都会经常遇到的问题。在过去,当遇到这类情况时,程序会终止执行并显示错误信息,通常是…...
mysql3.7之触发器
1.触发器的定义 触发器是由事件来触发某个操作,这些事件包括INSERT、UPDATE、DELETE事件。所谓事件就是指用户的动作或者触发某项行为。如果定义了触发程序,当数据库执行这些语句时候,就相当于事件发生了,就会自动激发触发器执行…...
12.QT文件对话框 文件的弹窗选择-QFileDialog
目录 前言: 技能: 内容: 1. 界面 2.信号槽 3.其他函数 参考: 前言: 通过按钮实现文件弹窗选择以及关联的操作 效果图就和平时用电脑弹出的选文件对话框一样 技能: QString filename QFileDialog::ge…...
ArcGIS学习(八)基于GIS平台的控规编制办法
ArcGIS学习(八)基于GIS平台的控规编制办法 上一任务我们学习了”如何进行图片数据的矢量化?" 这一关我们来学习一个比较简单的案例一一”如何在ArcGIS中录入控规指标,绘制控规图纸?" 首先,先来看看这个案例的分析思路以及导入CAD格式的控规图纸。 接着,来看…...
软件测试-自动化测试-面试题研究,知识要点,高频、重点知识点,自动化测试知识要点、知识梳理-PYTHON+自动化,评估试题
自动化项目实战能力评估 介绍一下你的自动化测试框架? 我的框架主要根据分层思想设计了几个独立模块: 模块一:主要存放通用业务代码,比如接口访问,数据库操作,excel 操作,等等 模块二…...
一.重新回炉Spring Framework: 理解Spring IoC
1. 写在前面的话 说实话,从事java开发工作时间也不短了,对于Spring Framework,也是天天用,这期间也碰到了很多问题,也解决了很多问题。可是,总感觉对Spring Framework还是一知半解,不能有个更加…...
目标检测算法之YOLOv5的应用实例(零售业库存管理、无人机航拍分析、工业自动化领域应用的详解)
1.YOLOv5在"零售业库存管理"领域的应用 在零售业库存管理中,YOLOv5可以帮助自动化商品识别和库存盘点过程。通过使用深度学习模型来实时识别货架上的商品,零售商可以更高效地管理库存,减少人工盘点的时间和成本。以下是一个使用YOLOv5进行商品识别的Python脚本示…...
[蓝桥 2017]九宫幻方
九宫幻方 题目描述 小明最近在教邻居家的小朋友小学奥数,而最近正好讲述到了三阶幻方这个部分,三阶幻方指的是将 1~9 不重复的填入一个 3*3 的矩阵当中,使得每一行、每一列和每一条对角线的和都是相同的。 三阶幻方又被称作九宫格…...
Qt - 编译报错:“invalid use of incomplete type ‘class Ui::xxx‘ui(new Ui::xxx)”的解决方法
问题起因 今天在创建Qt设计器界面类时,类名的英文拼写错误,然后就重命名了文件,而Qt Creator也帮我自动修改了一部分内容,之后我手动将cpp文件中的#include " *** "里的内容给修改了,构造函数和析构函数处也…...
基于Doris构建亿级数据实时数据分析系统
背景 随着公司业务快速发展,对业务数据进行增长分析的需求越来越迫切,与此同时我们的业务数据量也在快速激增、每天的数据新增量大概在30w 左右,一年就会产生1 个亿的数据,显然基于传统MySQL数据库已经无法支撑满足以上需求 基于上…...
javascript中的prototype;javascript中的原型链
文章目录 深入理解JavaScript原型链1. 什么是原型链?2. 原型链的结构3. 如何访问原型链?4. 示例演示原型链5. 原型链与继承6. 实际应用场景 深入理解JavaScript原型链 1. 什么是原型链? 在JavaScript中,每个对象都有一个原型&am…...
CI/CD部署
什么是CI,什么是CD CI和CD是软件开发中持续集成和持续交付的缩写。 CI代表持续集成(Continuous Integration),是一种实践,旨在通过自动化构建、测试和代码静态分析等过程,频繁地将代码变更合并到共享存储…...
定点数,定点数二维向量,定点数三维向量,定点数数学类
定点数,定点数二维向量,定点数三维向量,定点数数学类 介绍浮点数定点数封装的定点数FixedNumber定点数二维向量定点数三维向量定点数数学类总结 介绍 众所周知定点数是用于做帧同步时保持不同cpu不同设备保持一致稳定的代替浮点数的变量&…...
安装ts-node有感
起因:想要在vsCode上运行ts脚本 解决方案: 1.安装vsCode插件 code runner 2.全局安装ts-node 这一步遇到三个问题: ①.node版本问题:需安装版本18以上node,可使用nvm去控制不同的node版本 ②.certificate has exp…...
飞天使-k8s知识点18-kubernetes实操3-pod的生命周期
文章目录 探针的生命周期流程图prestop 探针的生命周期 docker 创建:在创建阶段,你需要选择一个镜像来运行你的应用。这个镜像可以是公开的,如 Docker Hub 上的镜像,也可以是你自己创建的自定义镜像。创建自己的镜像通常需要编写一…...
顺子日期 蓝桥杯
调用API 思路: 设置Calendar的属性,获取Calendar的毫秒数,转换成指定格式的字符串(yyyyMMdd),判断字符串中是否包含符合条件的,若有就1, 迭代: 每次循环给Calendar加上一天即可 import java.text.SimpleDateFormat; im…...
基于 Python 的景区票务人脸识别系统,附源码
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…...
OpenAI全新发布的Sora,到底意味着什么?
16日凌晨,OpenAI发布了文本视频的工具(text-do-video)Sora,整个世界再次被震撼。 Sora的出现,到底意味着什么? 目录 Sora的背景与概述Sora是什么?能为我们做些什么?存在的一些问题 文…...
预防.locked.locked1勒索病毒攻击:保护数据安全
导言: 随着科技的发展,网络安全问题日益严重,其中勒索病毒是一种令人头痛的威胁。.locked和.locked1是两种常见的勒索病毒,它们会将用户的数据文件加密,并要求支付赎金以获取解密密钥。本文将介绍这两种勒索病毒的特点…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
Sklearn 机器学习 缺失值处理 获取填充失值的统计值
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...
【深度学习新浪潮】什么是credit assignment problem?
Credit Assignment Problem(信用分配问题) 是机器学习,尤其是强化学习(RL)中的核心挑战之一,指的是如何将最终的奖励或惩罚准确地分配给导致该结果的各个中间动作或决策。在序列决策任务中,智能体执行一系列动作后获得一个最终奖励,但每个动作对最终结果的贡献程度往往…...
TCP/IP 网络编程 | 服务端 客户端的封装
设计模式 文章目录 设计模式一、socket.h 接口(interface)二、socket.cpp 实现(implementation)三、server.cpp 使用封装(main 函数)四、client.cpp 使用封装(main 函数)五、退出方法…...
【中间件】Web服务、消息队列、缓存与微服务治理:Nginx、Kafka、Redis、Nacos 详解
Nginx 是什么:高性能的HTTP和反向代理Web服务器。怎么用:通过配置文件定义代理规则、负载均衡、静态资源服务等。为什么用:提升Web服务性能、高并发处理、负载均衡和反向代理。优缺点:轻量高效,但动态处理能力较弱&am…...
JS面试常见问题——数据类型篇
这几周在进行系统的复习,这一篇来说一下自己复习的JS数据结构的常见面试题中比较重要的一部分 文章目录 一、JavaScript有哪些数据类型二、数据类型检测的方法1. typeof2. instanceof3. constructor4. Object.prototype.toString.call()5. type null会被判断为Obje…...
【Ragflow】26.RagflowPlus(v0.4.0):完善解析逻辑/文档撰写模式全新升级
概述 在历经半个月的间歇性开发后,RagflowPlus再次迎来一轮升级,正式发布v0.4.0。 开源地址:https://github.com/zstar1003/ragflow-plus 更新方法 下载仓库最新代码: git clone https://github.com/zstar1003/ragflow-plus.…...
