簡體   English   中英

當我嘗試測試我的重試工具 function 時,jest.advanceTimersByTime 不起作用

[英]jest.advanceTimersByTime doesn't work when I try to test my retry util function

我有一個我想測試的重試工具 function。 看起來像這樣

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();
};

它所做的是在給定的時間間隔內重試 function 一段時間。

我想我可以使用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);
  });
});

但似乎無論我將計時器提前多少,function 都只會被調用一次。

您可以在https://codesandbox.io/s/lucid-knuth-e810e?file=/src/index.test.ts找到代碼沙盒上的代碼

但是,codesandbox 存在一個已知問題,它不斷拋出此錯誤TypeError: jest.advanceTimersByTime is not a function 此錯誤不會出現在本地。

就是因為這個

這是我在測試助手文件中使用的內容:

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 ,我await我的advanceTimersByTime function。

在您的具體示例中,您只需要在調用 AdvanceTimersByTime 后await advanceTimersByTime - 如下所示:

// 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);


我有點晚了,但我今天必須解決這個問題,我通過制作這個新的實用程序 function 解決了這個問題。

// So we can wait setTimeout loops
export const advanceTimersByNTimes = (n = 1, time = 1000) => {
  for (let i = 0; i < n; i++) {
    act(() => {
      jest.advanceTimersByTime(time * 1);
    });
  }
};

這就是我在測試中使用它的方式:

await waitFor(() => {
  expect(screen.queryByTestId("timeout-exceeded-container")).toBeNull();
});

advanceTimersByNTimes(11);

await waitFor(() => {
  expect(screen.queryByTestId("timeout-exceeded-container")).not.toBeNull();
});

沒有其他東西對我有用,包括這里的答案。


這是我的代碼中沒有上述技巧就無法工作的部分(供參考):

setTimeout(() => setSeconds((prev) => prev + 1), 1000);

無論我將 jest.advanceTimersByTime 設置為什么,它都會步進到 2 倍,這些調用需要包裝在act塊中才能計數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM