说到mock,大家第一个想到的肯定是项目里经常用来模拟接口返回值的 mockjs库 ,Jest里的mock有所不同,下面会举一些例子来分别讲一讲 jest.fn()、jest.mock()、jest.spyOn()。
一、jest.fn()
jest.fn() 用于创建一个函数,我们可以设置该函数的返回值、监听该函数的调用、改变函数的内部实现等等,我们通过 jest.fn() 创建的函数有一个特殊的 .mock 属性,该属性保存了每一次调用情况,例子:
test('test jest.fn', () => {
// 通过jest.fn创建一个接收两个参数,返回俩参数相加的值的函数
const myMock = jest.fn((x, y) => x + y);
// 打印myMock调用次数
console.log(myMock.mock.calls.length); // 0
myMock(1, 2);
console.log(myMock.mock.calls.length); // 1
// 打印myMock第一次调用的第一个和第二个参数
console.log(myMock.mock.calls[0][0], myMock.mock.calls[0][1]); // 1 2
// 打印myMock第一次调用的返回值
console.log(myMock.mock.results[0].value); // 3
myMock('a', 'b');
console.log(myMock.mock.calls.length); // 2
// 打印myMock第二次调用的第一个和第二个参数
console.log(myMock.mock.calls[1][0], myMock.mock.calls[1][1]); // a b
// 打印myMock第二次调用的返回值
console.log(myMock.mock.results[1].value); // ab
const obj = {
name: 'add',
fn: myMock
}
obj.fn(3, 4)
// 打印myMock第三次调用的实例对象的name属性值
console.log(myMock.mock.instances[2].name); // add
// 强制修改myMock的返回值
myMock.mockReturnValueOnce(10).mockReturnValueOnce('x').mockReturnValue(true);
console.log(myMock(undefined, undefined), myMock(undefined, undefined), myMock(undefined, undefined), myMock(undefined, undefined)); // 10 x true true
})
二、jest.mock()
这个 jest.mock() 看官网老是看不明白,后来看了很多外部资料大概搞懂了。下面将假设一个场景并用代码实现一下,假设我们要测试math.ts里的myAdd函数,这个myAdd函数里调用了来自utils.ts文件的一个模块,而这个utils.ts是一个外部依赖文件或者是已经测试完毕的内部文件,这个时候我们就可以使用 jest.mock() 来模拟整个utils.ts文件(此时utils.ts里各个模块的监听就能做到了,也可以修改返回值什么的),以达到简化测试math.ts里myAdd函数的目的。
理一下上面的逻辑,我们模拟的应当是一个已经测试通过了的或是无需测试的文件/模块。下面用代码来实现一下:
// utils.ts
// 该文件包含add、sub等工具函数,且已经测试通过
export const add = (num1: number, num2: number) => {
return num1 + num2
}
export const sub = (num1: number, num2: number) => {
return num1 - num2
}
// math.ts
// 我们要测试myAdd函数,但是又不想关心add函数的内部实现
import { add } from "./utils";
const myAdd = (a: number, b: number) => {
return add(a, b);
}
export default myAdd;
这个时候我们要写测试文件mock.test.ts了:
import myAdd from "./math";
import { add } from "./utils";
// jest.mock("./utils.ts");
test("mock整个utils.ts模块", () => {
console.log(myAdd(1, 2));
expect(add).toBeCalledTimes(1);
});
注意下,上面是把 jest.mock(“./utils.ts”) 注释掉了的,看看结果:
可以看到成功打印出了3,但报了一个错,说接收的add函数不是mock或spy函数,无法监听被调用的次数。我们把上面注释掉的那行代码打开, 重新运行,结果如下:
打印的值变成了undefined,报错也消失了。这是由于mock了utils.ts整个模块后,add函数也变成了一个返回undefined的普通函数,不过我们可以自己设置它的返回值:
import myAdd from "./math";
import { add } from "./utils";
jest.mock("./utils.ts", () => {
return {
add: jest.fn(() => 3)
}
});
test("mock整个utils.ts模块", () => {
console.log(myAdd(1, 2));
expect(add).toBeCalledTimes(1);
});
三、jest.spyOn()
jest.spyOn() 我个人理解是把一个普通函数变成一个可监听的函数,并不改变内部实现(若想改变spyOn函数的具体实现,可使用mockImplementation)。这个理解不一定对,直接看例子吧:
import myAdd from "./math";
const utils = require('./utils')
const spy = jest.spyOn(utils, 'add');
test("mock整个utils.ts模块", () => {
console.log(myAdd(1, 2));
expect(spy).toBeCalledTimes(1);
});
结果为:
可以看到,utils模块里的add函数可以被成功执行(打印出了3),也可以被监听(未报错)。