Trying to test an exponential backoff method which retries API request via fetch 5 times, will have the following delays: [1 ms, 10 ms, 100 ms, 1 s, 10 s], I am not able to successfully test it.
Methods
export const delay = retryCount => new Promise(resolve => setTimeout(resolve, 10 ** retryCount));
/**
* Fetching with delay when api call fails,
* first 5 retries will have the following delays: [1 ms, 10 ms, 100 ms, 1 s, 10 s]
*/
export const fetchRetry = async (options, retryCount = 0, lastError = null) => {
if (retryCount > 5) throw new Error(lastError);
try {
return await fetch(options);
} catch (error) {
await delay(retryCount);
return fetchRetry(options, retryCount + 1, error);
}
};
Test
import fetchMock from 'jest-fetch-mock';
import { delay, fetchRetry } from './retry';
// This can be set up globally if needed
fetchMock.enableMocks();
beforeEach(() => {
fetch.resetMocks();
});
describe('fetchWithExponentialBackoffRetry', () => {
it('fetch is called once when response is 200', done => {
fetch.mockResponseOnce(
JSON.stringify({
success: true,
message: 'OK',
code: 200,
data: 'c86e795f-fe70-49be-a8fc-6876135ab109',
}),
);
setTimeout(function() {
fetchRetry({
inventory_type_id: 2,
advertiser_id: 2315,
file: null,
});
expect(fetch).toHaveBeenCalledTimes(1);
done();
}, 0);
});
it('fetch is called 5 times when response is returns failure', done => {
fetch.mockReject(() => Promise.reject(new Error('Rejected')));
setTimeout(function() {
fetchRetry({
inventory_type_id: 2,
advertiser_id: 2315,
file: null,
});
expect(fetch).toHaveBeenCalledTimes(5);
done();
}, 100000);
});
});
I am getting the following error
console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29 Error: Error: connect ECONNREFUSED 127.0.0.1:8
I think it has to do we delay method I have to incorporate setTimeout somehow in my tests, now sure how to mock it here. I would appreciate the help.
You're testing the outcome of an async function, so you need to make your tests async too - you're not doing so - i.e. you're not awaiting fetchRetry and are therefore just calling done() synchronously.
I think the error is being caused by the use of setTimeout here. This looks like a race condition bug so hard to be sure without debugging, but from reading the code it looks like the issue is that you're mocking fetch with jest-fetch-mock, but since your test code runs synchronously and you have...
beforeEach(() => {
fetch.resetMocks();
});
...it is probably unsetting the fetch mock before the call in the test that runs first is made and so it's actually calling your API - hence the error.
Making the tests async is fairly simple - the docs are here - and using async/await it's even cleaner since you don't actually need to use done - the test is just done when the promise resolves (or rejects).
Basically your test code will be mostly the same, except you'll be awaiting your calls to fetchRetry, like so:
it('fetch is called once when response is 200', async () => {
fetch.mockResponseOnce(...)
await fetchRetry({ ... })
expect(fetch).toHaveBeenCalledTimes(1);
});
it('fetch is called 5 times when response is returns failure', async () => {
fetch.mockReject(...);
try {
await fetchRetry({ ... });
} catch (err) {
// eventual error expected as response failure is mocked
expect(fetch).toHaveBeenCalledTimes(5);
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With