【问题标题】:How properly test setTimeout after Promise resolvePromise 解决后如何正确测试 setTimeout
【发布时间】:2020-04-10 16:37:55
【问题描述】:

如何正确测试此功能。

  1. 设置超时
  2. 超时后回调
  3. 和递归调用
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);
  });
});

但最后一个期望返回 1

【问题讨论】:

    标签: javascript typescript testing jestjs tdd


    【解决方案1】:

    不要专注于针对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);
      });
    });
    

    一些细节。

    1. 我很惊讶,但是在没有jest.clearAllTimers() 的情况下,it() 和我之间的计时器不会被清除。可能,这取决于jsdom 实现或jest 版本,我不知道。
    2. 没有await Promise.resolve() 你对callback 的模拟不会运行.then 部分。实际上它可能是await &lt;anything else&gt;,我只是看到await Promise.resolve(); 看起来没有await 42; 那么神奇。无论如何,它的目的是刷新microtasks queue,同时在上面开玩笑does not provide 直接API。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-10-13
      • 1970-01-01
      • 2021-07-10
      • 2018-03-24
      • 1970-01-01
      • 2014-12-21
      相关资源
      最近更新 更多