当前位置: 首页 > news >正文

【实战】一、Jest 前端自动化测试框架基础入门(四) —— 前端要学的测试课 从Jest入门到TDD BDD双实战(四)

文章目录

    • 一、Jest 前端自动化测试框架基础入门
      • 10.Jest 中的 Mock
        • (1)toBeCalled
        • (2)func.mock
        • (3)mockReturnValue & mockReturnValueOnce


学习内容来源: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 前端自动化测试框架基础入门(三)

10.Jest 中的 Mock

新建 lesson8.js

export const runCallback = callback => {callback();
}

要测试 runCallback 一般思路都是创建一个函数,返回一个值,只要 expect 最终拿到这个值就说明没问题,但是 runCallback 的主要功能是执行 callback,若是和本示例这样没有将 callback 返回,岂不是测不了了。。。

因此最佳实践来了,如下。

(1)toBeCalled

新建 lesson8.test.js

import { runCallback } from "./lesson8";test('测试 runCallback', () => {const func = jest.fn();runCallback(func);expect(func).toBeCalled();  
})

mock 一个函数,使用 toBeCalled 即可检测函数是否被调用

(2)func.mock

打印看一下 func.mock 里面有啥(console.log(func.mock)):

{calls: [ [] ],instances: [ undefined ],invocationCallOrder: [ 1 ],results: [ { type: 'return', value: undefined } ]
}
  • calls:func 每次执行的入参列表的列表
  • instances:每次执行 func 时创建的实例的列表(即 函数中this的指向,默认 undefined)
  • invocationCallOrder:每次 func 执行的顺序列表(例如:1, 2, 3 表示按顺序执行)
  • results:每次执行 func 的返回值列表

编辑 lesson8.test.js(执行两次 func)

import { runCallback } from "./lesson8";test('测试 runCallback', () => {const func = jest.fn();runCallback(func);runCallback(func);expect(func).toBeCalled();  console.log(func.mock)
})

再看一下 func.mock 里面有啥:

{calls: [ [], [] ],instances: [ undefined, undefined ],invocationCallOrder: [ 1, 2 ],results: [{ type: 'return', value: undefined },{ type: 'return', value: undefined }]
}

可以看到每一次调用在 func.mock 中都是留有痕迹的

编辑 lesson8.test.js(给 func 传入一个函数,并执行三次)

test('测试 runCallback', () => {const func = jest.fn(() => 123); // mock 函数,捕获函数的调用// func.mockImplementation(() => 123); // 功能同上一句// func.mockImplementationOnce(() => 123); // 只模拟一次// func.mockImplementationOnce(() => 456); // 只模拟一次// func.mockImplementation(() => this);// func.mockReturnThis(); // 作用同上一句runCallback(func);runCallback(func);runCallback(func);expect(func).toBeCalled();// expect(func).toBeCalledWith(); // 断言每次调用的入参内容console.log(func.mock)
})

打印结果:

{calls: [ [], [], [] ],instances: [ undefined, undefined, undefined ],invocationCallOrder: [ 1, 2, 3 ],results: [{ type: 'return', value: 123 },{ type: 'return', value: 123 },{ type: 'return', value: 123 }]
}

注意:带 Once 的要优先于不带的执行

(3)mockReturnValue & mockReturnValueOnce

编辑 lesson8.test.js(让 func 通过 mockReturnValueOnce 来返回值)

test('测试 runCallback', () => {const func = jest.fn(); // mock 函数,捕获函数的调用func.mockReturnValueOnce('once')runCallback(func);runCallback(func);runCallback(func);expect(func).toBeCalled();console.log(func.mock)
})

打印结果:

{calls: [ [], [], [] ],instances: [ undefined, undefined, undefined ],invocationCallOrder: [ 1, 2, 3 ],results: [{ type: 'return', value: 'once' },{ type: 'return', value: undefined },{ type: 'return', value: undefined }]
}

可以看到 'once' 只返回了一次

编辑 lesson8.test.js(让 func 通过 mockReturnValueOnce, mockReturnValueOnce 链式调用,mockReturnValue 三种方式来返回值)

test('测试 runCallback', () => {const func = jest.fn(); // mock 函数,捕获函数的调用func.mockReturnValueOnce('1');func.mockReturnValueOnce('2');func.mockReturnValueOnce('3').mockReturnValueOnce('4').mockReturnValueOnce('5');func.mockReturnValue('6'); // 后续每次返回同样的值[...new Array(8)].map(() => runCallback(func))expect(func).toBeCalled();console.log(func.mock)
})

打印结果:

{calls: [[], [], [], [],[], [], [], []],instances: [undefined, undefined,undefined, undefined,undefined, undefined,undefined, undefined],invocationCallOrder: [1, 2, 3, 4,5, 6, 7, 8],results: [{ type: 'return', value: '1' },{ type: 'return', value: '2' },{ type: 'return', value: '3' },{ type: 'return', value: '4' },{ type: 'return', value: '5' },{ type: 'return', value: '6' },{ type: 'return', value: '6' },{ type: 'return', value: '6' }]
}

接下来通过示例理解一下 mock 里的 instances

编辑 lesson8.js(callback 运行一次,之后创建一个实例,并赋予一个属性作为标识)

export const runCallback = (callback, index) => {callback();let obj = new callback()obj.name = 'callback_' + index
}

编辑 lesson8.test.js

test('测试 runCallback', () => {const func = jest.fn(); // mock 函数,捕获函数的调用func.mockReturnValue('6');  // 后续每次返回同样的值[...new Array(3)].map((item, index) => runCallback(func, index))expect(func).toBeCalled();console.log(func.mock)
})

打印结果:

{calls: [ [], [], [], [], [], [] ],instances: [undefined,mockConstructor { name: 'callback_0' },undefined,mockConstructor { name: 'callback_1' },undefined,mockConstructor { name: 'callback_2' }],invocationCallOrder: [ 1, 2, 3, 4, 5, 6 ],results: [{ type: 'return', value: '6' },{ type: 'return', value: undefined },{ type: 'return', value: '6' },{ type: 'return', value: undefined },{ type: 'return', value: '6' },{ type: 'return', value: undefined }]
}

可见每次运行默认不会有实例化对象,因此是 undefined,但是在实例化后,就会有一个命名为 mockConstructor 的构造对象

普通函数可以通过 jest.fn() 的方式来 mock,为保证当前部分功能的独立性,那接口请求也是需要 mock 的(避免网络和后端的影响)

编辑 lesson8.js(新增 getData 请求接口)

export const getData = () => {return axios.get('/api').then(res => res.data)
}

编辑 lesson8.test.js

import { runCallback, getData } from "./lesson8";
import axios from 'axios'jest.mock('axios');...test.only('测试 getData', async () => {axios.get.mockResolvedValue({data: 'hello'})await getData().then(data => {expect(data).toBe('hello')})
})

注意:jest.mock 必须放在文件的上面紧贴着 import,且外面不能嵌套任何内容否则对其mock的内容的作用域有影响(与被mock内容保持一致,且加载顺序挨着)

总结一下 mock 的几大功能:

  1. 捕获函数的调用和返回结果,以及 this 和调用顺序
  2. 可以自由设置返回结果
  3. 改变函数的内部实现

注意,从25版本开始,官方文档的结构发生了一些变化,最后一个变化前的版本:

  • https://archive.jestjs.io/docs/en/24.x/api

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

相关文章:

  • 【Java万花筒】事件溯源:探索完整状态历史记录的奇妙之旅
  • Django后端开发——模型层及ORM介绍
  • disql备份还原
  • 飞天使-k8s知识点18-kubernetes实操3-pod的生命周期
  • 普中51单片机学习(八)
  • UE4 C++联网RPC教程笔记(一)(第1~4集)
  • 生成式 AI - Diffusion 模型的数学原理(4)
  • CVE-2022-24652 漏洞复现
  • 嵌入式面试:瑞芯微
  • 【ArcGIS微课1000例】0103:导出点、线、面要素的折点坐标值
  • Code Composer Studio (CCS) - Breakpoint (断点)
  • 【数据结构与算法】图的搜索——广度优先遍历、最小生成树
  • Java基础知识学习:深入理解Java中的类与对象,Java重要知识点概念性解释,结合实例讲解请看下一篇博文
  • Ansible file文件模块 设置文件的属性,比如创建文件、创建链接文件、删除文件
  • 《白话C++》第10章 STL和boost,Page88 std::shared_ptr常用功能02
  • [LeetCode] Wiggle Sort
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • HTTP--网络协议分层,http历史(二)
  • input的行数自动增减
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • MobX
  • MySQL用户中的%到底包不包括localhost?
  • rabbitmq延迟消息示例
  • ReactNative开发常用的三方模块
  • spring cloud gateway 源码解析(4)跨域问题处理
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • 阿里云前端周刊 - 第 26 期
  • 计算机在识别图像时“看到”了什么?
  • 前嗅ForeSpider教程:创建模板
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 深入 Nginx 之配置篇
  • 使用Gradle第一次构建Java程序
  • 微服务核心架构梳理
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • 怎么将电脑中的声音录制成WAV格式
  • MPAndroidChart 教程:Y轴 YAxis
  • 带你开发类似Pokemon Go的AR游戏
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • #define 用法
  • #include
  • (10)ATF MMU转换表
  • (Forward) Music Player: From UI Proposal to Code
  • (poj1.2.1)1970(筛选法模拟)
  • (二)丶RabbitMQ的六大核心
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • (学习日记)2024.01.19
  • (一)认识微服务
  • (一)为什么要选择C++
  • (转)Sublime Text3配置Lua运行环境
  • .NET DataGridView数据绑定说明
  • .net 流——流的类型体系简单介绍