Mock相关API
Jest 中有三个与 Mock函数相关的API,分别是jest.fn()、jest.spyOn()、jest.mock()。使用它们创建Mock函数能够帮助我们更好的测试项目中一些逻辑较复杂的代码,例如测试函数的嵌套调用,回调函数的调用等。
Mock函数提供的以下三种特性
1、捕获函数调用情况
2、设置函数返回值
3、改变函数的内部实现
jest.fn()
jest.fn()是创建Mock函数最简单的方式,如果没有定义函数内部的实现,jest.fn()会返回undefined作为返回值。
test('测试jest.fn()调用', () => {
let mockFn = jest.fn();
let result = mockFn(1, 2, 3);
// 断言mockFn的执行后返回undefined
expect(result).toBeUndefined();
// 断言mockFn被调用
expect(mockFn).toBeCalled();
// 断言mockFn被调用了一次
expect(mockFn).toBeCalledTimes(1);
// 断言mockFn传入的参数为1, 2, 3
expect(mockFn).toHaveBeenCalledWith(1, 2, 3);
})
jest.fn()所创建的Mock函数还可以设置返回值,定义内部实现或返回Promise对象。
// functions.test.js
test('测试jest.fn()返回固定值', () => {
let mockFn = jest.fn().mockReturnValue('default');
// 断言mockFn执行后返回值为default
expect(mockFn()).toBe('default');
})
test('测试jest.fn()内部实现', () => {
let mockFn = jest.fn((num1, num2) => {
return num1 * num2;
})
// 断言mockFn执行后返回100
expect(mockFn(10, 10)).toBe(100);
})
test('测试jest.fn()返回Promise', async () => {
let mockFn = jest.fn().mockResolvedValue('default');
let result = await mockFn();
// 断言mockFn通过await关键字执行后返回值为default
expect(result).toBe('default');
// 断言mockFn调用后返回的是Promise对象
expect(Object.prototype.toString.call(mockFn())).toBe("[object Promise]");
})
jest.mock()
已经封装的请求方法可能我们在其他模块被调用的时候,并不需要进行实际的请求(请求方法已经通过单测或需要该方法返回非真实数据)。此时,使用jest.mock()去mock整个模块是十分有必要的。
/ fetch.js
import axios from 'axios';
export default {
async fetchPostsList(callback) {
return axios.get('https://jsonplaceholder.typicode.com/posts').then(res => {
return callback(res.data);
})
}
}
// events.js
import fetch from './fetch';
export default {
async getPostList() {
return fetch.fetchPostsList(data => {
console.log('fetchPostsList be called!');
});
}
}
// functions.test.js
import events from '../src/events';
import fetch from '../src/fetch';
jest.mock('../src/fetch.js');
test('mock 整个 fetch.js模块', async () => {
expect.assertions(2);
await events.getPostList();
expect(fetch.fetchPostsList).toHaveBeenCalled();
expect(fetch.fetchPostsList).toHaveBeenCalledTimes(1);
});
在jest中如果想捕获函数的调用情况,则该函数必须被mock或者spy
jest.spyOn()
jest.spyOn()方法同样创建一个mock函数,但是该mock函数不仅能够捕获函数的调用情况,还可以正常的执行被spy的函数。实际上,jest.spyOn()是jest.fn()的语法糖,它创建了一个和被spy的函数具有相同内部代码的mock函数。
console.log('fetchPostsList be called!');这行代码并没有被打印,这是因为通过jest.mock()后,模块内的方法是不会被jest所实际执行的。这时我们就需要使用jest.spyOn()。
// functions.test.js
import events from '../src/events';
import fetch from '../src/fetch';
test('使用jest.spyOn()监控fetch.fetchPostsList被正常调用', async() => {
expect.assertions(2);
const spyFn = jest.spyOn(fetch, 'fetchPostsList');
await events.getPostList();
expect(spyFn).toHaveBeenCalled();
expect(spyFn).toHaveBeenCalledTimes(1);
})
总结
在实际项目的单元测试中,jest.fn()常被用来进行某些有回调函数的测试;jest.mock()可以mock整个模块中的方法,当某个模块已经被单元测试100%覆盖时,使用jest.mock()去mock该模块,节约测试时间和测试的冗余度是十分必要;当需要测试某些必须被完整执行的方法时,常常需要使用jest.spyOn()。这些都需要开发者根据实际的业务代码灵活选择。