关键词:
Jest 使用指南 - - Mock 篇
#jest
Jest Mock
为什么会用到 Mock? Mock 能帮我们解决什么问题?
在项目中,一个模块的方法内常常会去调用另外一个模块的方法。在单元测试中,我们可能并不需要关心内部调用的方法的执行过程和结果,只想知道它是否被正确调用即可,甚至会指定该函数的返回值。此时,使用Mock函数是十分有必要。
Mock函数提供的以下三种特性,在我们写测试代码时十分有用:
- 擦除函数的实际实现(换句话说:改变函数的内部实现)
- 捕获函数调用情况( 包括:这些调用中传递的参数、new 的实例)
- 设置函数返回值
jest.fn()
Jest.fn()是创建Mock函数最简单的方式,如果没有定义函数内部的实现,jest.fn()会返回undefined作为返回值。
Jest.fn()所创建的Mock函数还可以设置返回值,定义内部实现或返回Promise对象。
test("测试jest.fn()返回固定值", () =>
let mockFn = jest.fn().mockReturnValue("Felix");
// 断言 mockFn 执行后返回值为Felix
expect(mockFn()).toBe("Felix");
myMock.mockReturnValueOnce(10).mockReturnValueOnce('x').mockReturnValue(true);
// > 10 'x' true
);
test("测试jest.fn()内部实现", () =>
let mockFn = jest.fn((x) => 42 + x);
// 断言mockFn执行后返回 52
expect(mockFn(10)).toBe(52);
);
test("测试jest.fn()返回Promise", async () =>
let mockFn = jest.fn().mockResolvedValue("Felix");
let result = await mockFn();
// 断言mockFn通过await关键字执行后返回值为Felix
expect(result).toBe("Felix");
// 断言mockFn调用后返回的是Promise对象
expect(Object.prototype.toString.call(mockFn())).toBe("[object Promise]");
);
jest.mock()
ES6 Class Mocks · Jest
有下面几种方式来 mock 模块
- 使用 jest.mock 自动 mock
jest.mock('./utils.ts')
自动返回一个 mock ,可以使用它来监视对类构造函数及其所有方法的调用。方法调用保存在中
theAutomaticMock.mock.instances[index].methodName.mock.calls
。
⚠️:如果您在类中使用箭头函数,则它们不会成为模拟的一部分。这样做的原因是,箭头函数不存在于对象的原型中,它们只是持有对该函数的引用的属性。 - jest.mock()直接在单元测试里面mock 模块
jest.mock(path, moduleFactory)
接受模块工厂参数。模块工厂是一个返回模拟的函数。为了模拟构造函数,模块工厂必须返回构造函数。换句话说,模块工厂必须是返回函数的函数-高阶函数(HOF)。
例如在 node 端会通过 fs 来读取文件,在单元测试中, 我们并不需要真去调用fs读取文件, 就可以考虑把fs模块mock掉, 如下代码:
jest.mock('fs', () => (
readFileSync: jest.fn()
))
⚠️:这么使用有个限制就是,因为调用 jest.mock() 被提升到了文件开头,因此不可以先定义一个变量,然后在工厂中使用它。以 "mock "开头的变量是一个例外。这取决于你是否能保证它们按时被初始化!
3. 在需要mock的模块目录临近建立目录__mocks__
对于用户目录下面的模块
例如我们需要mock目录models下面的user模块,那么我们就需要在models下面新建__mocks__目录(这里要区分大小写),然后新建文件user.js。
注意:用这种方式, 需要在单元测试文件中需添加下面的代码才能使此mock生效。jest.mock('./moduleName')
// mock.ts
import utils from "./utils";
export default
test()
return utils.add(1, 2);
,
;
// utils.ts
export default
add(a: number, b: number): number
console.log("----", a, b);
return a + b;
,
;
// mock.test.ts
import m from "./mock";
import utils from "./utils";
jest.mock("./utils.ts");
test("mock 整个 fetch.js模块", () =>
m.test();
expect(y).toBeCalledTimes(1);
);
终端结果
jest.spyOn()
Jest.spyOn()方法同样创建一个mock函数,但是该mock函数不仅能够捕获函数的调用情况,还可以正常的执行被spy的函数。实际上,jest.spyOn()是jest.fn()的语法糖,它创建了一个和被spy的函数具有相同内部代码的mock函数。
import m from "./mock";
import utils from "./utils";
test("mock 整个 fetch.js模块", () =>
const y = jest.spyOn(utils, "add");
m.test();
expect(y).toBeCalledTimes(1);
);
上面 jest.mock() 终端截图中可以看到,console 并没有打印,这是因为通过jest.mock()后,模块内的方法是不会被jest所实际执行的。这时我们就需要使用jest.spyOn()。
Time mock
我们时常会用到 setTimeout 、setInterval、 clearTimeout 、clearInterval,针对这些定时器我们怎么去控制单测呢。
根据官方文旦time mock的说明,这里可以分为三种 Run All time(快进所有的定时时间)、Advance Timer by Time(快进指定的时间)、Run Pending Timer(针对嵌套了定时器的场景)
function timerGame(callback)
console.log('Ready....go!');
setTimeout(() =>
console.log("Time's up -- stop!");
callback && callback();
, 1000);
module.exports = timerGame;
Run All Timer
jest.useFakeTimers();
test('calls the callback after 1 second', () =>
const timerGame = require('../timerGame');
const callback = jest.fn();
timerGame(callback);
// At this point in time, the callback should not have been called yet
expect(callback).not.toBeCalled()
// Fast-forward until all timers have been executed
jest.runAllTimers();
// Now our callback should have been called!
expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1);
);
注意:这里我们通过调用 jest.useFakeTimers(); 来启用假定时器。这用模拟函数模拟了 setTimeout 和其他计时器函数。
Advance TImer by Times
原先的方法名和上面的 runAllTimers 是呼应的,叫做 runTimersToTime,在 v22.0.0 后重命名为 advanceTimersByTime ,这个函数的作用就是快进指定的时间,
it('calls the callback after 1 second via advanceTimersByTime', () =>
const timerGame = require('../timerGame');
const callback = jest.fn();
timerGame(callback);
// At this point in time, the callback should not have been called yet
expect(callback).not.toBeCalled();
// Fast-forward until all timers have been executed
jest.advanceTimersByTime(1000);
// Now our callback should have been called!
expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1);
);
就上面这个而言,其实和 runAllTimer 是一样的,因为我们指定的时间就是全部的时间,下面我们可以尝试一下设置另外的时间
it("calls the callback after 999 millsecond via advanceTimersByTime", () =>
const callback = jest.fn();
timerGame(callback);
expect(callback).not.toBeCalled();
// 改为 999 毫秒,不到 1秒
jest.advanceTimersByTime(999);
expect(callback).not.toBeCalled();
expect(callback).toHaveBeenCalledTimes(0);
// 又快进了 1 毫秒
jest.advanceTimersByTime(1);
expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1);
);
上面的例子我们将指定时间改为。999 毫秒,不到 1 秒,所以相应的回调函数还没执行,根据运行结果的确是的,之后我们又只快进了 1毫秒,对应的 callback 执行了,符合我们的预期
最后一种 Run Pending Timer 就不多讲了,遇到了再来看,相对来说上面两种使用的场景较多
总结
在实际项目的单元测试中,jest.fn()常被用来进行某些有回调函数的测试;jest.mock()可以mock整个模块中的方法,当某个模块已经被单元测试100%覆盖时,使用jest.mock()去mock该模块,节约测试时间和测试的冗余度是十分必要;当需要测试某些必须被完整执行的方法时,常常需要使用jest.spyOn()。这些都需要开发者根据实际的业务代码灵活选择。
jest中的mock,jest.fn()jest.spyon()jest.mock()(代码片段)
...有必要。Mock函数提供的以下三种特性,在我们写测试代码时十分有用:捕获函数 查看详情
前端自动化测试框架jest中的mock(代码片段)
众所周知,在前面的代码中,我们在编写一个测试用例的时候用到了test、expect、toBe三个方法,在之前我们也介绍了Jest的基础使用、Jest的简单配置、Jest中的匹配器(matchers)、Jest命令行工具的使用和异步代码... 查看详情
jest使用指南--基础篇(代码片段)
Jest使用指南--基础篇#jestJest基础Jest语法descriptionDescribe,一种用于包含一个或多个相关测试的Jest方法。每次在开始为功能编写新的测试套件时,都将其包装在describe块中。description('一组测试的名称',()=> //测试的方法... 查看详情
jest使用指南--tsx组件篇(代码片段)
Jest使用指南--tsx组件篇#jest首先考虑一下,React组件我们需要测试哪些东西呢?React组件的render结果是一个组件树,并且整个树最终会被解析成一个纯粹由HTML元素构成的树形结构React组件可以拥有state,且state的变化... 查看详情
jest(代码片段)
...库,大部分其它工具将让你在测试中(甚至经常在你的主代码中)写一些不尽如人意的样板代码,以使其生效。Jest与Jasmine框架的区别是在后者之上增加了一些层。最值得注意的是,运行测试时,Jest会自动模拟依赖。Jest自动为... 查看详情
jest使用指南--ts文件篇(代码片段)
Jest使用指南--ts文件篇#jestts文件的单测ts文件主要是一些工具方法的使用,然后对于其中的工具方法进行单测,比对返回的数据结构,包括基础数据类型、对象、数组、函数异步方法这部分直接参考测试异步代码·Jest... 查看详情
jest使用指南--ts文件篇(代码片段)
Jest使用指南--ts文件篇#jestts文件的单测ts文件主要是一些工具方法的使用,然后对于其中的工具方法进行单测,比对返回的数据结构,包括基础数据类型、对象、数组、函数异步方法这部分直接参考测试异步代码·Jest... 查看详情
每次测试都是mock模块(代码片段)
我对在Jest中嘲笑如何对实现进行单元测试感到困惑。问题是我想模仿不同的预期行为。有没有办法实现这个目标?因为导入只能在文件的顶部,并且能够模拟在导入之前必须声明的内容。我也尝试传递一个本地函数,所以我可... 查看详情
jest-mock-extended - 使用对象输入调用模拟 [Typescript]
...题描述】:我在测试中使用jest-mock-extended。我想测试以下代码:classAconstructor(privateb:B)publictest(x:string): 查看详情
如何使用 jest.mock 模拟 useRef 和反应测试库
...回模拟当前值。我试过jest.mock但它返回一个HTMLDivElement。代码:constref=useRef<HTMLDivElement|nul 查看详情
vue开发实战实战篇#35:如何高效地使用mock数据进行开发(代码片段)
说明【Vue开发实战】学习笔记。效果我们通过mock数据来进行开发,比如下面的图表数据来自我们的mock数据调用接口http://localhost:8080/api/dashboard/chart?id=kaimo313安装依赖npminstall--saveaxios编写mock新建ant-design-vue-pro\\mock\\dashboard_c... 查看详情
jest使用指南--基础篇(代码片段)
Jest使用指南--基础篇#jestJest基础Jest语法descriptionDescribe,一种用于包含一个或多个相关测试的Jest方法。每次在开始为功能编写新的测试套件时,都将其包装在describe块中。description('一组测试的名称',()=> //测试的方法... 查看详情
使用 jest.mock('axios') 时如何模拟拦截器?
】使用jest.mock(\\\'axios\\\')时如何模拟拦截器?【英文标题】:Howtomockinterceptorswhenusingjest.mock(\'axios\')?使用jest.mock(\'axios\')时如何模拟拦截器?【发布时间】:2020-06-1003:18:56【问题描述】:使用jest运行测试时,我有基本的测试套装... 查看详情
jest使用指南--tsx组件篇(代码片段)
Jest使用指南--tsx组件篇#jest首先考虑一下,React组件我们需要测试哪些东西呢?React组件的render结果是一个组件树,并且整个树最终会被解析成一个纯粹由HTML元素构成的树形结构React组件可以拥有state,且state的变化... 查看详情
使用 jest 时 axios mock 无法处理默认标头
...测试时都访问服务器。这是它自己的action.js文件中的操作代码。login(commit,user)r 查看详情
spock单元测试框架实战指南七-动态mock(代码片段)
这篇讲解Spock自带的mock功能如何和powermock组合使用,发挥更强大的作用动态Mock静态方法(Spock Where +PowerMock)在上一篇的例子中使用powermock让静态方法返回一个指定的值,那能不能每次返回不同的值呢?我们先看下什么场景... 查看详情
使用 Jest 模拟的服务导致“不允许 jest.mock() 的模块工厂引用任何超出范围的变量”错误
】使用Jest模拟的服务导致“不允许jest.mock()的模块工厂引用任何超出范围的变量”错误【英文标题】:ServicemockedwithJestcauses"Themodulefactoryofjest.mock()isnotallowedtoreferenceanyout-of-scopevariables"error【发布时间】:2017-11-2219:11:13【问... 查看详情
jest.mock():如何使用工厂参数模拟 ES6 类默认导入
】jest.mock():如何使用工厂参数模拟ES6类默认导入【英文标题】:jest.mock():HowtomockES6classdefaultimportusingfactoryparameter【发布时间】:2018-05-0406:17:13【问题描述】:模拟ES6类导入我想在我的测试文件中模拟我的ES6类导入。如果被模拟... 查看详情