【问题标题】:jest.advanceTimersByTime doesn't work when I try to test my retry util function当我尝试测试我的重试 util 函数时,jest.advanceTimersByTime 不起作用
【发布时间】:2021-05-29 04:07:47
【问题描述】:

我有一个想要测试的重试实用程序功能。看起来是这样的

export const sleep = (t: number) => new Promise((r) => setTimeout(r, t));

type RetryFn = (
  fn: Function,
  config: {
    retryIntervel: number;
    retryTimeout: number;
    predicate: Function;
    onRetrySuccess?: Function;
    onRetryFail?: Function;
  }
) => Promise<any>;

export const retry: RetryFn = async (
  fn,
  { predicate, onRetrySuccess, onRetryFail, retryIntervel, retryTimeout }
) => {
  const startTime = Date.now();
  let retryCount = 0;
  while (Date.now() - startTime < retryTimeout) {
    try {
      const ret = await fn();
      if (predicate(ret)) {
        if (retryCount > 0) onRetrySuccess && onRetrySuccess();
        return ret;
      } else {
        throw new Error();
      }
    } catch {
      retryCount++;
    }
    await sleep(retryIntervel);
  }
  if (onRetryFail) onRetryFail();
};

它的作用是在给定的时间间隔内重试该函数。

我想我可以使用jest.advanceTimersByTime 来提前计时器以测试重试发生了多少次。

import { retry } from "./index";

const value = Symbol("test");

function mockFnFactory(numFailure: number, fn: Function) {
  let numCalls = 0;
  return function () {
    fn();
    numCalls++;
    if (numCalls <= numFailure) {
      console.log("numCalls <= numFailure");

      return Promise.resolve({ payload: null });
    } else {
      console.log("numCalls => numFailure");

      return Promise.resolve({
        payload: value
      });
    }
  };
}

describe("retry function", () => {
  beforeEach(() => {
    jest.useFakeTimers();
  });
  it("retrys function on 1st attempt, and succeed thereafter", async () => {
    const fn = jest.fn();
    const onRetrySuccessFn = jest.fn();
    const mockFn = mockFnFactory(3, fn);
    retry(mockFn, {
      predicate: (res: any) => res.payload === value,
      onRetrySuccess: onRetrySuccessFn,
      retryIntervel: 1000,
      retryTimeout: 5 * 60 * 1000
    });
    jest.advanceTimersByTime(1000);
    expect(fn).toHaveBeenCalledTimes(1);
    expect(onRetrySuccessFn).not.toHaveBeenCalled();
    jest.advanceTimersByTime(1000);
    expect(fn).toHaveBeenCalledTimes(2); // ???? fail
    expect(onRetrySuccessFn).not.toHaveBeenCalled();
    jest.advanceTimersByTime(2000);
    expect(fn).toHaveBeenCalledTimes(3);// ???? fail
    expect(onRetrySuccessFn).toHaveBeenCalledTimes(1);
  });
});

但似乎无论我将计时器提前多少,该函数都只会被调用一次。

您可以在https://codesandbox.io/s/lucid-knuth-e810e?file=/src/index.test.tshttps://codesandbox.io/s/lucid-knuth-e810e?file=/src/index.test.tscodesandbox 上找到代码

但是,有一个已知的 issue 带有代码框,它不断抛出此错误 TypeError: jest.advanceTimersByTime is not a function 。此错误不会在本地出现。

【问题讨论】:

标签: javascript typescript unit-testing jestjs


【解决方案1】:

因为this

这是我在测试帮助文件中使用的内容:

const tick = () => new Promise(res => setImmediate(res));

export const advanceTimersByTime = async time => jest.advanceTimersByTime(time) && (await tick());

export const runOnlyPendingTimers = async () => jest.runOnlyPendingTimers() && (await tick());
 
export const runAllTimers = async () => jest.runAllTimers() && (await tick());

在我的测试文件中,我导入了我的助手,而不是调用jest.advanceTimersByTime,而是我的awaitadvanceTimersByTime 函数。

在你的具体例子中,你只需要在调用advanceTimersByTime 之后给await 一个函数——就像这样:

// top of your test file
const tick = () => new Promise(res => setImmediate(res));

... the rest of your existing test file

    jest.advanceTimersByTime(1000);
    expect(fn).toHaveBeenCalledTimes(1);
    expect(onRetrySuccessFn).not.toHaveBeenCalled();
    jest.advanceTimersByTime(1000);
    await tick(); // this line
    expect(fn).toHaveBeenCalledTimes(2);
    expect(onRetrySuccessFn).not.toHaveBeenCalled();
    jest.advanceTimersByTime(2000);
    await tick(); // this line
    expect(fn).toHaveBeenCalledTimes(3)
    expect(onRetrySuccessFn).toHaveBeenCalledTimes(1);


【讨论】:

  • 您好,感谢您的回答!我以前不知道这个 API setImmediate 。在 MDN 上查找它,它说它是实验性的。我想在测试文件中使用它可以吗?
  • @Joji - 当然,这只是为了测试。你查看我在 jest 的 github 上链接的评论了吗?
  • @Joji - 另外,值得注意的是,它可以在使用 setTimeout(fn,0) 的浏览器中合理地伪造,尽管它是 fully supported in nodejs
猜你喜欢
  • 2018-09-08
  • 1970-01-01
  • 2013-08-26
  • 1970-01-01
  • 2018-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多