Promise resolve 后如何正确测试 setTimeout
How properly test setTimeout after Promise resolve
如何正确测试此功能。
- 设置超时
- 超时回调
- 和递归调用
export function initScheduler(timeout: number, callback: () => Promise<void>): void {
setTimeout(() => {
callback().then(() => {
initScheduler(timeout, callback);
});
}, timeout);
}
我试过
describe('initScheduler', () => {
it('should call on schedule', () => {
jest.useFakeTimers();
const timeout: number = 60000;
const callback: jest.Mock = jest.fn().mockResolvedValue(undefined);
initScheduler(timeout, callback);
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), timeout);
expect(callback).not.toBeCalled();
jest.advanceTimersByTime(timeout);
expect(callback).toBeCalled();
expect(setTimeout).toHaveBeenCalledTimes(2);
});
});
但最后的期望returns1
不要专注于针对 setTimeout
的断言。专注于您的功能预期执行的操作。
describe('', () => {
jest.useFakeTimers();
const timeout = 300;
const callback = jest.fn();
beforeEach(() => {
jest.clearAllTimers();
callback
.mockClear()
.mockReturnValue(Promise.resolve());
});
it('runs callback only after delay given', () => {
initScheduler(timeout, callback);
jest.advanceTimersByTime(timeout - 1);
expect(callback).not.toHaveBeenCalled();
jest.advanceTimersByTime(2);
expect(callback).toHaveBeenCalledTimes(1);
});
it('reruns scheduler if callback been resolved successfully', async () => {
initScheduler(timeout, callback);
expect(callback).not.toHaveBeenCalled();
jest.advanceTimersByTime(timeout);
await Promise.resolve();
jest.advanceTimersByTime(timeout);
await Promise.resolve();
jest.advanceTimersByTime(timeout);
await Promise.resolve();
expect(callback).toHaveBeenCalledTimes(3);
});
it('stops scheduler if callback rejected', async () => {
callback.mockReturnValue(Promise.reject());
initScheduler(timeout, callback);
jest.advanceTimersByTime(timeout);
await Promise.resolve();
jest.advanceTimersByTime(timeout);
await Promise.resolve();
expect(callback).toHaveBeenCalledTimes(1);
});
});
一些细节。
- 我很惊讶,但在没有
jest.clearAllTimers()
的情况下,计时器在 it()
之间没有被清除。可能取决于 jsdom
实现或 jest
版本,我不知道。
- 没有
await Promise.resolve()
你的模拟 callback
没有 运行 .then
部分。实际上它可能是 await <anything else>
,我只是看到 await Promise.resolve();
看起来没有 await 42;
那么神奇。不管怎样,它的目的是直接刷新 microtasks queue while jest itself does not provide API 就可以了。
如何正确测试此功能。
- 设置超时
- 超时回调
- 和递归调用
export function initScheduler(timeout: number, callback: () => Promise<void>): void {
setTimeout(() => {
callback().then(() => {
initScheduler(timeout, callback);
});
}, timeout);
}
我试过
describe('initScheduler', () => {
it('should call on schedule', () => {
jest.useFakeTimers();
const timeout: number = 60000;
const callback: jest.Mock = jest.fn().mockResolvedValue(undefined);
initScheduler(timeout, callback);
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), timeout);
expect(callback).not.toBeCalled();
jest.advanceTimersByTime(timeout);
expect(callback).toBeCalled();
expect(setTimeout).toHaveBeenCalledTimes(2);
});
});
但最后的期望returns1
不要专注于针对 setTimeout
的断言。专注于您的功能预期执行的操作。
describe('', () => {
jest.useFakeTimers();
const timeout = 300;
const callback = jest.fn();
beforeEach(() => {
jest.clearAllTimers();
callback
.mockClear()
.mockReturnValue(Promise.resolve());
});
it('runs callback only after delay given', () => {
initScheduler(timeout, callback);
jest.advanceTimersByTime(timeout - 1);
expect(callback).not.toHaveBeenCalled();
jest.advanceTimersByTime(2);
expect(callback).toHaveBeenCalledTimes(1);
});
it('reruns scheduler if callback been resolved successfully', async () => {
initScheduler(timeout, callback);
expect(callback).not.toHaveBeenCalled();
jest.advanceTimersByTime(timeout);
await Promise.resolve();
jest.advanceTimersByTime(timeout);
await Promise.resolve();
jest.advanceTimersByTime(timeout);
await Promise.resolve();
expect(callback).toHaveBeenCalledTimes(3);
});
it('stops scheduler if callback rejected', async () => {
callback.mockReturnValue(Promise.reject());
initScheduler(timeout, callback);
jest.advanceTimersByTime(timeout);
await Promise.resolve();
jest.advanceTimersByTime(timeout);
await Promise.resolve();
expect(callback).toHaveBeenCalledTimes(1);
});
});
一些细节。
- 我很惊讶,但在没有
jest.clearAllTimers()
的情况下,计时器在it()
之间没有被清除。可能取决于jsdom
实现或jest
版本,我不知道。 - 没有
await Promise.resolve()
你的模拟callback
没有 运行.then
部分。实际上它可能是await <anything else>
,我只是看到await Promise.resolve();
看起来没有await 42;
那么神奇。不管怎样,它的目的是直接刷新 microtasks queue while jest itself does not provide API 就可以了。