入门部分
在项目中,一个模块的方法内常常会去调用另外一个模块的方法。在单元测试中,我们可能并不需要关心内部调用的方法的执行过程和结果,只想知道它是否被正确调用即可,甚至会指定该函数的返回值。此时,使用Mock函数是十分有必要。
jest
对象上有fn,mock,spyOn
三个方法,在实际项目的单元测试中,jest.fn()
常被用来进行某些有回调函数的测试;jest.mock()
可以mock
整个模块中的方法,当某个模块已经被单元测试100%覆盖时,使用jest.mock()
去mock
该模块,节约测试时间和测试的冗余度是十分必要;当需要测试某些必须被完整执行的方法时,常常需要使用jest.spyOn()
。
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
对象。
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]");
//上面这个实际上返回的是String对象,返回Promise对象的写法要怎么做呢?
})
下面我们在src/fetch.js
文件中写一些被测试代码,以更加接近业务的方式来理解Mock函数的实际应用。
// fetch.js
const axios = require('axios');
async function fetchPostsList(callback) {
return axios.get('https://jsonplaceholder.typicode.com/posts').then(res => {
return callback(res.data);
})
}
module.exports = fetchPostsList;
我们在fetch.js
中封装了一个fetchPostsList
方法,该方法请求了JSONPlaceholder
提供的接口,并通过传入的回调函数返回处理过的返回值。如果我们想测试该接口能够被正常请求,只需要捕获到传入的回调函数能够被正常的调用即可。下面是functions.test.js
中的测试的代码。
const fetch = require('../src/fetch');
test('fetchPostsList callback was called', async () => {
expect.assertions(1);
let mockFn = jest.fn();
await fetch(mockFn);
// 断言mockFn被调用
expect(mockFn).toBeCalled();
})
.mock
属性
所有的 mock
函数都有一个特殊的 .mock
属性,它保存了关于此函数如何被调用、调用时的返回值的信息。
test('test function forEach', () => {
const mockCallback = jest.fn(x => 88 + x);
forEach([0, 1], mockCallback);
// mock函数被调用的次数
expect(mockCallback.mock.calls.length).toBe(2);
// 第一次调用函数时的第一个参数是0
expect(mockCallback.mock.calls[0][0]).toBe(0);
// 第一次调用函数时第二个参数时mockCallback
// expect(mockCallback.mock.calls[0][1]).toBe(1);???
// 第二次调用函数时第一个参数是1
expect(mockCallback.mock.calls[1][0]).toBe(1);
// 第一次函数调用的返回值是88
expect(mockCallback.mock.results[0].value).toBe(88);
// 第二次函数调用的返回值是89
expect(mockCallback.mock.results[1].value).toBe(89);
})
Jest.mock()
通常情况下,我们需要调用api,发送ajax请求,从后台获取数据。但是我们在做前端测试的时候,并不需要去调用真实的接口,所以此时我们需要模拟 axios/fetch
模块,让它不必调用api也能测试我们的接口调用是否正确。
// events.js
const fetch = require('./fetch');
async function getPostList() {
return fetch(data => {
console.log('fetchPostsLits be called');
})
}
module.exports = getPostList;
测试代码如下:
const fetch = require('../src/fetch');
const event = require('../src/event');
jest.mock('../src/fetch.js');
test('fetchPostsList callback was called', async () => {
expect.assertions(1);
let mockFn = jest.fn();
await fetch(mockFn);
expect(mockFn).toBeCalled();
})
//这里会报错
/* Error: expect(received).toHaveBeenCalled()
Matcher error: received value must be a mock or spy function
Received has value: undefined
at Object.<anonymous> (/Users/giriawu/Documents/test_demo/getting-started-with-jest/__tests__/functions.test.js:15:21)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
*/
//该如何解决?
test('mock all fetch.js', async () => {
expect.assertions(2);
await event();
expect(fetch()).toHaveBeenCalled()
expect(fetch()).toHaveBeenCalledTimes(1);
})
在测试代码中我们使用了 jest.mock('axios')
去mock整个 fetch.js
模块。如果注释掉这行代码,执行测试脚本时会出现报错
注意: 在 jest
中如果想捕获函数的调用情况,则该函数必须被 mock
或者 spy
!
Jest.spyOn()
jest.spyOn()
方法同样创建一个mock
函数,但是该mock
函数不仅能够捕获函数的调用情况,还可以正常的执行被spy
的函数。实际上,jest.spyOn()
是jest.fn()
的语法糖,它创建了一个和被spy
的函数具有相同内部代码的mock
函数。
jest.spyOn()
方法创建一个mock函数,并且可以正常执行被spy的函数。 jest.spyOn()
是 jest.fn()
的语法糖,它创建了一个和被spy的函数具有相同内部代码
的mock函数。
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);
})
Timer Mock
jest
可以模拟定时器从而允许自主控制时间流逝。模拟定时器运行可以方便测试,比如不必等待一个很长的延时而是直接获取结果。
jest
对象上与timer mock
相关的方法主要有以下个:
jest.useFakeTimers()
:指示Jest
使用标准计时器函数的假版本(setTimeout,setInterval,clearTimeout,clearInterval,nextTick,setImmediate和clearImmediate
)。jest.useRealTimers()
:指示Jest
使用标准计时器功能的真实版本。jest.clearAllTimers()
:从计时器系统中清除任何等待的计时器。jest.runAllTicks()
:执行微任务队列中的所有任务(通常通过process.nextTick
在节点中连接)。jest.runAllTimers()
:执行宏任务队列中的所有任务。jest.runAllImmediates()
:通过setImmediate()
执行任务队列中的所有任务。jest.advanceTimersByTime(n)
:执行宏任务队列中的所有任务,当该API
被调用时,所有定时器被提前n
秒。jest.runOnlyPendingTimers()
:仅执行宏任务中当前正在等待的任务。
举例来说(见Example.5):
// timer.js
function timerGame(callback) {
console.log('Ready....go!');
setTimeout(() => {
console.log('Times up -- stop!');
callback && callback();
}, 1000);
}
module.exports = timerGame;
我们在timer.js
中设定了一个1s钟之后才会执行的定时器,但是测试代码是同步执行,通过timer mock
,我们不必等待定时器执行完成就可以完成测试。
const timer = require('./timer');
const callback = jest.fn();
jest.useFakeTimers();
test('calls the callback after 1 second', () => {
timer(callback);
expect(callback).not.toBeCalled();
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setInterval).toHaveBeenCalledTimes(0);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
jest.runAllTimers();
expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1);
});
在上面的代码中,我们已经在第四行声明使用假时间,在test
块中,虽然setTimeout
尚未执行完毕,但是测试已经完成,setTimeout
执行一次,没有setInterval
执行,这与期望一致。接下来调用jest.runAllTimers()
使得所有定时器立即执行完毕,控制台打印定时器中的输出。
对于递归定时器的情况,如果使用jest.runAllTimers()
,所有的定时器就无限循环了,这个时候就需要用到jest.runOnlyPendingTimers()
了,因为runOnlyPendingTimers
的过程中不会产生新的定时器,从而避免了无限循环的问题,如Example.5 中pendingTimer
所示。
jest.advanceTimersByTime(n)
也很容易理解,就是将所有定时器提前n
秒执行。如下所示,在未调用jest.advanceTimersByTime(n)
之前,callback
还没有被调用,然后通过jest.advanceTimersByTime(1000)
让定时器提前1s
执行,因此接下来的断言不会报错。
// timer3.test.js
const timerGame = require('./timer');
const callback = jest.fn();
jest.useFakeTimers();
test('calls the callback after 1 second via advanceTimersByTime', () => {
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);
});
同样的,如果使用jest.advanceTimersByTime(500)
提前0.5s
,上面的测试可以进行如下修改。
jest.advanceTimersByTime(500);
expect(callback).not.toBeCalled();
expect(callback).toHaveBeenCalledTimes(0);
Manual Mock
Manual Mock
用于存储模拟数据的功能。例如,您可能希望创建一个允许您使用虚假数据的手动模拟,而不是访问网站或数据库等远程资源。这可以确保您的测试快速且稳定。
通过在紧邻模块的__mocks __
子目录中编写模块来定义手动模拟。例如,要在models
目录中模拟一个名为user
的模块(如Example.9 所示),请创建一个名为user.js
的文件并将其放在models / __ mocks__
目录中。同时请注意__mocks__
文件夹区分大小写。
// userMocked.test.js
import user from './models/user';
jest.mock('./models/user');
test('if user model is mocked', () => {
expect(user.getAuthenticated()).toEqual({age: 622, name: 'Mock name'});
});
如果您正在模拟的模块是Node
模块(例如:lodash
),则mock
应放在与node_modules
相邻的__mocks__
目录中,并将自动模拟。没有必要显式调用jest.mock('module_name')
。但是如果想模拟Node
的核心模块(例如:fs
或path
),那么明确地调用例如: jest.mock('path')
是必需的,因为默认情况下不会模拟核心Node
模块。
如果您正在使用ES6
模块导入,那么您通常倾向于将导入语句放在测试文件的顶部。但是通常你需要指示Jest
在模块使用它之前使用模拟。出于这个原因,Jest
会自动将jest.mock
调用提升到模块的顶部(在任何导入之前)。
ES6 Class Mock
如Example.6中所示,分别有SoundPlayer
类和使用该类的SoundPlayerConsumer
类。我们将在SoundPlayerConsumer
的测试中模拟SoundPlayer
。
// sound-player.js
export default class SoundPlayer {
constructor() {
this.foo = 'bar';
}
playSoundFile(fileName) {
console.log('Playing sound file ' + fileName);
}
}
// sound-player-consumer.js
import SoundPlayer from './sound-player';
export default class SoundPlayerConsumer {
constructor() {
this.soundPlayer = new SoundPlayer();
}
playSomethingCool() {
const coolSoundFileName = 'song.mp3';
this.soundPlayer.playSoundFile(coolSoundFileName);
}
}
分别有4种方法可以创建ES6
类的模拟:
1.Automatic mock
使用该方法,类中的所有方法调用状态保存在theAutomaticMock.mock.instances[index].methodName.mock.calls
中。同时,如果您在类中使用了箭头函数,它们将不会成为模拟的一部分。因为箭头函数不存在于对象的原型上,它们只是包含对函数的引用的属性。
2.Manual mock
在相邻的__mocks__
文件夹下生成同名文件从而进行替换模拟。
3.Calling jest.mock() with the module factory parameter
使用此种方法就是在jest.mock()
中添加一个参数:jest.mock(path,moduleFactory)
接受moduleFactory
参数,moduleFactory
返回一个模拟的函数。为了模拟构造函数,moduleFactory
必须返回构造函数,换句话说,模块工厂必须是返回函数的函数。
4.Replacing the mock using mockImplementation() or mockImplementationOnce()
对jest.mock
的调用会被提升到代码的顶部。 您通过在现有mock
上调用mockImplementation()
(或mockImplementationOnce()
)而不是使用factory
参数,从而稍后指定模拟(例如,在beforeAll()
),或者在测试之间更改模拟。
Bypassing Module Mock
Jest
允许您模拟测试中的整个模块,这有助于测试代码是否正确,函数调用是否正确。但是,有时您可能希望在测试文件中使用模拟模块的一部分,在这种情况下,您希望访问原始实现,而不是模拟版本。jest.requireActual()
允许你导入实际的版本,而不是模拟的版本。
总结
通过mock函数我们可以通过以下三个特性去更好的编写我们的测试代码:
- 捕获函数调用情况
- 设置函数返回值
- 改变函数的内部实现
实际项目的单元测试中:
-
jest.fn()
常被用来进行某些有回调函数的测试; -
jest.mock()
可以mock整个模块中的方法,当某个模块已经被单元测试100%覆盖时,使用jest.mock()去mock该模块,节约测试时间和测试的冗余度是十分必要; -
jest.spyOn()
当需要测试某些必须被完整执行的方法时,常常需要使用;
这些都需要开发者根据实际的业务代码灵活选择。
官方文档部分
Mock 函数允许你测试代码之间的连接——实现方式包括:擦除函数的实际实现、捕获对函数的调用 ( 以及在这些调用中传递的参数) 、在使用 new
实例化时捕获构造函数的实例、允许测试时配置返回值。
有两种方法可以模拟函数:要么在测试代码中创建一个 mock 函数,要么编写一个手动 mock
来覆盖模块依赖。
使用mock函数
假设我们要测试函数 forEach
的内部实现,这个函数为传入的数组中的每个元素调用一次回调函数。
function forEach(items, callback) {
for (let index = 0; index < items.length; index++) {
callback(items[index]);
}
}
为了测试此函数,我们可以使用一个 mock 函数,然后检查 mock 函数的状态来确保回调函数如期调用。
const mockCallback = jest.fn(x => 42 + x);
forEach([0, 1], mockCallback);
// 此 mock 函数被调用了两次
expect(mockCallback.mock.calls.length).toBe(2);
// 第一次调用函数时的第一个参数是 0
expect(mockCallback.mock.calls[0][0]).toBe(0);
// 第二次调用函数时的第一个参数是 1
expect(mockCallback.mock.calls[1][0]).toBe(1);
// 第一次函数调用的返回值是 42
expect(mockCallback.mock.results[0].value).toBe(42);
.mock
属性
所有的 mock 函数都有这个特殊的 .mock
属性,它保存了关于此函数如何被调用、调用时的返回值的信息。 .mock
属性还追踪每次调用时 this
的值,所以我们同样可以也检视(inspect) this
:
const myMock = jest.fn();
const a = new myMock();
const b = {};
const bound = myMock.bind(b);
bound();
console.log(myMock.mock.instances);
// > [ <a>, <b> ]
这些 mock 成员变量在测试中非常有用,用于说明这些 function 是如何被调用、实例化或返回的:
// 这个函数只调用一次
expect(someMockFunction.mock.calls.length).toBe(1);
// 这个函数被第一次调用时的第一个 arg 是 'first arg'
expect(someMockFunction.mock.calls[0][0]).toBe('first arg');
// 这个函数被第一次调用时的第二个 arg 是 'second arg'
expect(someMockFunction.mock.calls[0][1]).toBe('second arg');
// 这个函数被实例化两次
expect(someMockFunction.mock.instances.length).toBe(2);
// 这个函数被第一次实例化返回的对象中,有一个 name 属性,且被设置为了 'test’
expect(someMockFunction.mock.instances[0].name).toEqual('test');
Mock的返回值
Mock 函数也可以用于在测试期间将测试值注入代码︰
const myMock = jest.fn();
console.log(myMock());
// > undefined
myMock.mockReturnValueOnce(10).mockReturnValueOnce('x').mockReturnValue(true);
console.log(myMock(), myMock(), myMock(), myMock());
// > 10, 'x', true, true
在函数连续传递风格(functional continuation-passing style)的代码中时,Mock 函数也非常有效。 以这种代码风格有助于避免复杂的中间操作,便于直观表现组件的真实意图,这有利于在它们被调用之前,将值直接注入到测试中。
const filterTestFn = jest.fn();
// Make the mock return `true` for the first call,
// and `false` for the second call
filterTestFn.mockReturnValueOnce(true).mockReturnValueOnce(false);
const result = [11, 12].filter(num => filterTestFn(num));
console.log(result);
// > [11]
console.log(filterTestFn.mock.calls[0][0]); // 11
console.log(filterTestFn.mock.calls[1][0]); // 12
大多数现实世界例子中,实际是在依赖的组件上配一个模拟函数并配置它,但手法是相同的。 在这些情况下,尽量避免在非真正想要进行测试的任何函数内实现逻辑。
模拟模块
假定有个从 API 获取用户的类。 该类用 axios 调用 API 然后返回 data
,其中包含所有用户的属性:
// users.js
import axios from 'axios';
class Users {
static all() {
return axios.get('/users.json').then(resp => resp.data);
}
}
export default Users;
为测试该方法而不实际调用 API (使测试缓慢与脆弱),我们可以用 jest.mock(...)
函数自动模拟 axios 模块。
一旦模拟模块,我们可为 .get
提供一个 mockResolvedValue
,它会返回假数据用于测试。相当于,我们想要axios.get('/users.json')
返回一个假的response
// users.test.js
import axios from 'axios';
import Users from './users';
jest.mock('axios');
test('should fetch users', () => {
const users = [{name: 'Bob'}];
const resp = {data: users};
axios.get.mockResolvedValue(resp);
// or you could use the following depending on your use case:
// axios.get.mockImplementation(() => Promise.resolve(resp))
return Users.all().then(data => expect(data).toEqual(users));
});
Mock实现
在某些情况下用Mock函数替换指定返回值是非常有用的。 可以用 jest.fn
或 mockImplementationOnce
方法来实现Mock函数。
const myMockFn = jest.fn(cb => cb(null, true));
myMockFn((err, val) => console.log(val));
// > true
当你需要根据别的模块定义默认的Mock函数实现时,mockImplementation
方法是非常有用的。
// foo.js
module.exports = function () {
// some implementation;
};
// test.js
jest.mock('../foo'); // this happens automatically with automocking
const foo = require('../foo');
// foo is a mock function
foo.mockImplementation(() => 42);
foo();
// > 42
当你需要模拟某个函数调用返回不同结果时,请使用 mockImplementationOnce
方法︰
const myMockFn = jest
.fn()
.mockImplementationOnce(cb => cb(null, true))
.mockImplementationOnce(cb => cb(null, false));
myMockFn((err, val) => console.log(val));
// > true
myMockFn((err, val) => console.log(val));
// > false
当 mockImplementationOne
定义的实现逐个调用完毕时, 如果定义了jest.fn
,它将使用 jest.fn
。
const myMockFn = jest
.fn(() => 'default')
.mockImplementationOnce(() => 'first call')
.mockImplementationOnce(() => 'second call');
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());
// > 'first call', 'second call', 'default', 'default'
大多数情况下,我们的函数调用都是链式的,如果你希望创建的函数支持链式调用(因为返回了this),可以使用.mockReturnThis()
函数来支持。
const myObj = {
myMethod: jest.fn().mockReturnThis(),
};
// is the same as
const otherObj = {
myMethod: jest.fn(function () {
return this;
}),
};
Mock名称
- .mockName()
你可以为你的Mock函数命名,该名字会替代 jest.fn()
在单元测试的错误输出中出现。 用这个方法你就可以在单元测试输出日志中快速找到你定义的Mock函数。
const myMockFn = jest
.fn()
.mockReturnValue('default')
.mockImplementation(scalar => 42 + scalar)
.mockName('add42');
自定义匹配器
测试Mock函数需要写大量的断言,为了减少代码量,我们提供了一些自定义匹配器。
// The mock function was called at least once
expect(mockFunc).toHaveBeenCalled();
// The mock function was called at least once with the specified args
expect(mockFunc).toHaveBeenCalledWith(arg1, arg2);
// The last call to the mock function was called with the specified args
expect(mockFunc).toHaveBeenLastCalledWith(arg1, arg2);
// All calls and the name of the mock is written as a snapshot
expect(mockFunc).toMatchSnapshot();
这些匹配器是断言Mock函数的语法糖。 你可以根据自己的需要自行选择匹配器。
// The mock function was called at least once
expect(mockFunc.mock.calls.length).toBeGreaterThan(0);
// The mock function was called at least once with the specified args
expect(mockFunc.mock.calls).toContainEqual([arg1, arg2]);
// The last call to the mock function was called with the specified args
expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1]).toEqual([
arg1,
arg2,
]);
// The first arg of the last call to the mock function was `42`
// (note that there is no sugar helper for this specific of an assertion)
expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1][0]).toBe(42);
// A snapshot will check that a mock was invoked the same number of times,
// in the same order, with the same arguments. 它还会在名称上断言。
expect(mockFunc.mock.calls).toEqual([[arg1, arg2]]);
expect(mockFunc.getMockName()).toBe('a mock name');
常用方法总结
Mock
函数常用方法:
-
mockFn.mockName(value)
:设置mock
函数的名字 -
mockFn.getMockName()
: 返回mockFn.mockName(value)
中设置的名字 -
mockFn.mock.calls
:mock
函数的调用信息
mockFn.mock.calls
返回一个数组,数组中的每一个元素又是一个数组,包含mock
函数的调用信息。比如,一个被调用两次的模拟函数f,参数为f('arg1','arg2')
,然后使用参数f('arg3','arg4')
,mockFn.mock.calls
返回的数组形式如下:
因此,mockFn.mock.calls.length
代表mock
函数被调用次数,mockFn.mock.calls[0][0]
代表第一次调用传入的第一个参数,以此类推。
-
mockFn.mock.results
:mock
函数的return
值,以数组存储 -
mockFn.mock.instances
:mock
函数实例
const mockFn = jest.fn();
const a = new mockFn();
const b = new mockFn();
mockFn.mock.instances[0] === a; // true
mockFn.mock.instances[1] === b; // true
mockFn.mockImplementation(fn)
:创建一个mock
函数
注意:jest.fn(implementation)
是jest.fn().mockImplementation(implementation)
的简写。
mockFn.mockImplementationOnce(fn)
:创建一个mock
函数
该函数将用作对mocked
函数的一次调用的mock
的实现。可以链式调用,以便多个函数调用产生不同的结果。
const myMockFn = jest
.fn()
.mockImplementationOnce(cb => cb(null, true))
.mockImplementationOnce(cb => cb(null, false));
myMockFn((err, val) => console.log(val)); // true
myMockFn((err, val) => console.log(val)); // false
当mocked
函数用完使用mockImplementationOnce
定义的实现时,如果调用它们,它将使用jest.fn(()=> defaultValue)
或.mockImplementation(()=> defaultValue)
执行默认实现集:
const myMockFn = jest
.fn(() => 'default')
.mockImplementationOnce(() => 'first call')
.mockImplementationOnce(() => 'second call');
// 'first call', 'second call', 'default', 'default'
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());
mockFn.mockReturnThis()
:jest.fn()
的语法糖
jest.fn(function() {
return this;
});
mockFn.mockReturnValue(value)
:接受一个值作为调用mock
函数时的返回值
const mock = jest.fn();
mock.mockReturnValue(42);
mock(); // 42
mock.mockReturnValue(43);
mock(); // 43
mockFn.mockReturnValueOnce(value)
:接受一个值作为调用mock
函数时的返回值,可以链式调用,以便产生不同的结果。
当不再使用mockReturnValueOnce
值时,调用将返回mockReturnValue
指定的值。
const myMockFn = jest
.fn()
.mockReturnValue('default')
.mockReturnValueOnce('first call')
.mockReturnValueOnce('second call');
// 'first call', 'second call', 'default', 'default'
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());
mockFn.mockResolvedValue(value)
:mock
异步函数的语法糖
实现上类似于
jest.fn().mockImplementation(() => Promise.resolve(value));
用于在test
中模拟异步函数
test('async test', async () => {
const asyncMock = jest.fn().mockResolvedValue(43);
await asyncMock(); // 43
});
mockFn.mockResolvedValueOnce(value)
:语法糖
实现上类似于
jest.fn().mockImplementationOnce(() => Promise.resolve(value));
test('async test', async () => {
const asyncMock = jest
.fn()
.mockResolvedValue('default')
.mockResolvedValueOnce('first call')
.mockResolvedValueOnce('second call');
await asyncMock(); // first call
await asyncMock(); // second call
await asyncMock(); // default
await asyncMock(); // default
});
mockFn.mockRejectedValue(value)
:语法糖
实现上类似于
jest.fn().mockImplementation(() => Promise.reject(value));
test('async test', async () => {
const asyncMock = jest.fn().mockRejectedValue(new Error('Async error'));
await asyncMock(); // throws "Async error"
});
mockFn.mockRejectedValueOnce(value)
:语法糖
实现上类似于
jest.fn().mockImplementationOnce(() => Promise.reject(value));
test('async test', async () => {
const asyncMock = jest
.fn()
.mockResolvedValueOnce('first call')
.mockRejectedValueOnce(new Error('Async error'));
await asyncMock(); // first call
await asyncMock(); // throws "Async error"
});
mockFn.mockClear()
:重置所有存储在mockFn.mock.calls
和mockFn.mock.instances
数组中的信息
当你想要清除两个断言之间的模拟使用数据时,这通常很有用。
mockFn.mockReset()
:完成mockFn.mockClear()
所做的所有事情,还删除任何模拟的返回值或实现
当你想要将模拟完全重置回其初始状态时,这非常有用。(请注意,重置spy
将导致函数没有返回值)。
mockFn.mockRestore()
:完成mockFn.mockReset()
所做的所有事情,并恢复原始(非模拟)实现
当你想在某些测试用例中模拟函数并在其他测试用例中恢复原始实现时,这非常有用。
参考文档
-
Jest单元测试入门和实例 (juejin.cn)
-
使用Jest测试JavaScript(Mock篇) - SegmentFault 思否
-
Jest 入门指南 | 冰,水为之而寒于水 (iceiceice.top)
-
使用Jest测试JavaScript(Mock篇) - SegmentFault 思否
-
官方文档
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!