模拟计时器
模拟计时器
jest.spyOn
该方法用于模拟对象或者类的方法,并且可以监控这些方法的调用情况:
const myApi = {
async fetchUser(id) {
const response = await fetch(`/api/user/${id}`);
const user = await response.json();
return user;
}
};
test('fetches user data', async () => {
// 监视了 myApi 对象的 fetchUser 方法
const spy = jest.spyOn(myApi, 'fetchUser');
// 通过 spy 重新定义了该方法的返回值,从而达到一个更好的控制
spy.mockResolvedValue({ name: 'John', age: 30 });
const user = await myApi.fetchUser(123);
expect(user).toEqual({ name: 'John', age: 30 });
expect(spy).toHaveBeenCalledWith(123);
spy.mockRestore();
});
jest.spyOn用于监视对象的方法,可以监视被测试代码中的某一个方法是否被正确的调用,另外还可以控制所监视的方法的行为。jest.mock常用于一个模块的导入,可以在测试中替换掉被测试代码中所依赖的模块。
模拟setInterval
模拟测试setInterval,需要使用jest.useFakeTimers方法,方法的作用是提供了一个模拟的计时器对象,可以模拟时间的流逝。
假设代码中有一个setTimeout,该计时器是5秒后触发,如果使用真实的计时器,那么需要等待5秒,而如果使用模拟的计时器对象,那么可以对时间进行一个快进操作。
使用了jest.useFakeTimers,模拟了计时器之后,后面需要将计时器还原,可以使用jest.useRealTimers:
如下是一个模拟setInterval的测试示例:
- src/utils/tools.ts
- __test__/timer.spec.ts
export function startTimer(
callback: () => void,
interval: number
): NodeJS.Timeout {
const timerId = setInterval(() => {
callback()
}, interval)
return timerId
}
export function clearTimer(timerId: NodeJS.Timeout) {
clearInterval(timerId)
}
import { startTimer, clearTimer } from '../src/utils/tools'
beforeEach(() => {
jest.useFakeTimers()
})
afterEach(() => {
jest.useRealTimers()
})
test('Start timer', () => {
// 定时调用的回调函数
const callback = jest.fn()
// 间隔为1秒
const interval = 1000
// 使用模拟的setInterval, 避免调用真实的setInterval
const spy = jest.spyOn(window, 'setInterval')
// 开始计时器
const timerId = startTimer(callback, interval)
// 期望模拟的setInterval函数只调用一次
expect(spy).toHaveBeenCalledTimes(1)
// 期望模拟的setInterval调用时接收两个参数
// 第一个参数为函数
// 第二个参数为数字
expect(spy).toHaveBeenCalledWith(
expect.any(Function),
interval
)
// 将时间快进1秒
jest.advanceTimersByTime(interval)
// 期望callback调用1次
expect(callback).toHaveBeenCalledTimes(1)
// 将时间再快进1秒
jest.advanceTimersByTime(interval)
// 期望callback调用2次
expect(callback).toHaveBeenCalledTimes(2)
// 期望模拟的setInterval函数仍然只调用一次
expect(spy).toHaveBeenCalledTimes(1)
// 清除计时器
clearTimer(timerId)
})
test('Clear timer', () => {
const callback = jest.fn()
const interval = 1000
const timerId = startTimer(callback, interval)
// 清除计时器
clearTimer(timerId)
// 将时间快进1秒
jest.advanceTimersByTime(interval)
// 期望callback没有被调用
expect(callback).toHaveBeenCalledTimes(0)
expect(callback).not.toHaveBeenCalled()
})
- 通过调用
jest.useFakeTimers,使用模拟的计时器对象,其能够控制时间的流逝,在beforeEach里面调用,使其会在每一个测试用例开始之前对计时器对象进行替换。 - 通过调用
jest.useRealTimers,将模拟的计时器对象还原为真实的计时器对象,在afterEach里面调用,使其会在每一个测试用例结束之后换回来。 - 使用
jest.spyOn来监听setInterval方法,并且替换为了模拟的setInterval,这样可以避免调用真实的setInterval。
模拟setTimeout
setTimeout与setInterval类似:
- src/utils/tools.ts
- __test__/timeout.spec.ts
export function startTimeout(
callback: () => void,
timeout: number
): NodeJS.Timeout {
const timerId = setTimeout(() => {
callback()
}, timeout)
return timerId
}
export function stopTimeout(timerId: NodeJS.Timeout) {
clearTimeout(timerId)
}
import {
startTimeout,
stopTimeout
} from '../src/utils/tools'
beforeEach(() => {
jest.useFakeTimers()
})
afterEach(() => {
jest.useRealTimers()
})
test('Start timeout', () => {
const callback = jest.fn()
const timeout = 2000
const spy = jest.spyOn(window, 'setTimeout')
const timerId = startTimeout(callback, timeout)
expect(callback).not.toHaveBeenCalled()
jest.advanceTimersByTime(1000)
expect(callback).not.toHaveBeenCalled()
jest.advanceTimersByTime(1000)
expect(spy).toHaveBeenCalledTimes(1)
expect(callback).toHaveBeenCalledTimes(1)
stopTimeout(timerId)
})
test('Stop timeout', () => {
const callback = jest.fn()
const timeout = 2000
const spy = jest.spyOn(window, 'setTimeout')
const timerId = startTimeout(callback, timeout)
stopTimeout(timerId)
expect(spy).toHaveBeenCalledTimes(1)
expect(callback).not.toHaveBeenCalled()
})