Monday, December 11, 2023
 Popular · Latest · Hot · Upcoming
193
rated 0 times [  200] [ 7]  / answers: 1 / hits: 9701  / 3 Years ago, fri, february 26, 2021, 12:00:00

I have a retry util function I wanted to test for. It looks like this


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


what it does is retry the function for a period of time at a given interval.


I thought I could use jest.advanceTimersByTime to advance the timer to test how many times the retry happens.


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


but it seems like no matter how much I advanced the timer, the function only gets invoked once.


You can find the code on codesandbox at https://codesandbox.io/s/lucid-knuth-e810e?file=/src/index.test.ts


However, there is a known issue with codesandbox where it keeps throwing this error TypeError: jest.advanceTimersByTime is not a function . This error doesn't appear locally.


More From » typescript

 Answers
7

It's because of this.


Here's what I use in a test helpers file:


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

In my test file, I import my helpers and instead of calling jest.advanceTimersByTime, I await my advanceTimersByTime function.


In your specific example, you just need to await a function after calling advanceTimersByTime - like this:


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



[#1724] Monday, February 22, 2021, 3 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
monicag

Total Points: 651
Total Questions: 106
Total Answers: 104

Location: Grenada
Member since Sun, Dec 20, 2020
3 Years ago
monicag questions
Sun, Feb 6, 22, 00:00, 2 Years ago
Mon, Nov 29, 21, 00:00, 2 Years ago
Fri, Jan 15, 21, 00:00, 3 Years ago
Wed, Aug 5, 20, 00:00, 3 Years ago
;