softonic / axios-retry

Axios plugin that intercepts failed requests and retries them whenever possible
Other
1.9k stars 167 forks source link

Testing axios-retry implementation with Jest? #139

Open mesterjagels opened 4 years ago

mesterjagels commented 4 years ago

I'm having issues with testing our implementation of axios-retry with jest. We've had to implement our own RetryConfig to cover post calls to our graphql instance with this config:

axiosRetryConfig: IAxiosRetryConfig = {
  retries: 3,
  retryCondition: error => {
    const methodsToRetry = ['get', 'head', 'options', 'post'];
    return !!(error.config.method && methodsToRetry.includes(error.config.method) && axiosRetry.isNetworkError && axiosRetry.isRetryableError);
  },
  retryDelay: axiosRetry.exponentialDelay
};

I've tried the following, however it does not retry the call:

  it('retries calls when they fail', async () => {
    axiosRetry(axios, axiosRetryConfig);
    const errorObject = {
      code: '500',
      config: {
        method: 'get'
      }
    };
    const axiosSpy = spyOn(axios, 'get').and.returnValue(Promise.resolve(errorObject));

    await httpService.get(uri);

    await flushPromises();
    expect(axiosSpy).toBeCalledTimes(4);
  });

with the following outcome

HttpService  › retries calls when they fail

    expect(spy).toBeCalledTimes(expected)

    Expected number of calls: 4
    Received number of calls: 1

      106 | 
      107 |     await flushPromises();
    > 108 |     expect(axiosSpy).toBeCalledTimes(4);
          |                      ^
      109 |   });
      110 | 
      111 |   describe('GraphQL', () => {

      at Object.<anonymous> (tests/unit/services/http.service.spec.ts:108:22)

If i change the returnValue to returnValue(Promise.reject(errorObject)); i get the follow result:

● HttpService  › retries calls when they fail

    Failed: Object {
      "code": "500",
      "config": Object {
        "method": "get",
      },
    }

      93 |   });
      94 | 
    > 95 |   it('retries calls when they fail', async () => {
         |   ^
      96 |     axiosRetry(axios, axiosRetryConfig);
      97 |     const errorObject = {
      98 |       code: '500',

      at Env.it (node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:91:24)
      at Suite.<anonymous> (tests/unit/services/http.service.spec.ts:95:3)
      at Object.<anonymous> (tests/unit/services/http.service.spec.ts:11:1)

Anyone with experience in testing this plugin who would like to share a pointer or two?

coolboy0961 commented 4 years ago

same question.

mawrkus commented 4 years ago

Hi, Have you seen https://github.com/softonic/axios-retry/blob/master/spec/index.spec.js ?

AimalKhanOfficial commented 3 years ago

Is there any update on this? Stuck on the same problem. Axios registers a single call still. (as mentioned by @mesterjagels)

aliabbasmalik8 commented 3 years ago

any update on this issue?

aliabbasmalik8 commented 3 years ago

can manage like this way

describe('axiosRetry(axios, { retries, retryCondition })', () => {
  const AxiosInstance = axios.create()
  it('should execute for each retry', () => {
    let retryCount = 0
    axiosRetry(AxiosInstance, {
      retries: 3,
      retryCondition: error => {
        let flag = false
        // update flag on base of your conditions
        return flag
      },
      retryDelay: () => {
        retryCount += 1
        return 0
      },
    })

    AxiosInstance.get('http://example.com/test').then(() => {
      expect(retryCount).toBe(2)
    })
  })
})
bradlof commented 3 years ago

Would be interested in an update as well. I think the test above is only green because the assertion is never reached.

liveHarshit commented 3 years ago

Waiting for an update

JSEvgeny commented 2 years ago

Need an update on this one too

stasoft91 commented 2 years ago

Could be tested with Nock

Test:

  it('downloadMapper Success 3rd try', async () => {
    const scope = nock(mockHost)
      .get('/' + mockFilename)
      .delayConnection(400)
      .replyWithError('404')
      .get('/' + mockFilename)
      .delayConnection(400)
      .replyWithError('404')
      .get('/' + mockFilename)
      .delayConnection(400)
      .replyWithFile(200, __dirname + '/mocks/mockDownloadableFile.js');

    await downloaderMapper(mockHost + mockFilename);

    scope.done(); // contains all the neccessary 'expect`s'
  });

downloaderMapper:

export async function downloaderMapper(fileUrl: string): Promise<void> {
  axiosRetry(axios, { retries: 3 });

  const fileContentBuffer = await axios.get(fileUrl, {
    timeout: 5000,
    decompress: true,
    responseType: 'arraybuffer',
  });
}

Though I'm not sure if the exact number of retries could be tested because of race conditions, it tests the fact of retry. I'm so sure because with retries: 1 test fails.

safeimuslim commented 2 years ago

Hi, any best solution for this one?

stasoft91 commented 2 years ago

@safeimuslim every solution here is the best one, choose wisely =)

HaimCandiTech commented 2 years ago

Hi, any best solution for this one?

I think the best approach is to use nock and do like the way axios-retry test itself

kadishmal commented 3 months ago

Here is my test to validate axios-retry.

it('should fail retrying multiple times when timeout of 4000 ms occurs', async () => {
  const request: CheckServiceRecipientRequest = {
    account: {
      value: '996559692626',
    },
    id: 'e4121fc6-cdf2-4d6c-afb0-ba788112d094',
    service: '4',
  };

  let retryCount = 0;

  const axiosInterceptorSpy = jest.fn().mockImplementation((config) => {
    // Original Axios request does not include the `axios-retry` property.
    if (retryCount > 0) {
      expect(config['axios-retry']).toMatchObject({
        retries: 2,
        retryCount,
      });
    }

    ++retryCount;

    throw new AxiosError('timeout of 4000ms exceeded', 'ECONNABORTED', config);
  });

  const spyInterceptorId = axiosClient.interceptors.request.use(axiosInterceptorSpy);

  expect.assertions(6);

  try {
    await client.checkServiceRecipient(request);
  } catch (err) {
    expect(err).toBeInstanceOf(ErrorWithStatusCode);
    expect((err as ErrorWithStatusCode).statusCode).toEqual(StatusCode.GATEWAY_TIMEOUT);
    expect((err as ErrorWithStatusCode).message).toEqual('Request timed out.');

    expect(axiosInterceptorSpy).toHaveBeenCalledTimes(3);
  }

  axiosClient.interceptors.request.eject(spyInterceptorId);
});
chris-tuncap commented 3 months ago

Nock no longer works with axios-retry (4.5.0). It retries once and then hangs until the test suite times out.

Could be tested with Nock

Test:

  it('downloadMapper Success 3rd try', async () => {
    const scope = nock(mockHost)
      .get('/' + mockFilename)
      .delayConnection(400)
      .replyWithError('404')
      .get('/' + mockFilename)
      .delayConnection(400)
      .replyWithError('404')
      .get('/' + mockFilename)
      .delayConnection(400)
      .replyWithFile(200, __dirname + '/mocks/mockDownloadableFile.js');

    await downloaderMapper(mockHost + mockFilename);

    scope.done(); // contains all the neccessary 'expect`s'
  });

downloaderMapper:

export async function downloaderMapper(fileUrl: string): Promise<void> {
  axiosRetry(axios, { retries: 3 });

  const fileContentBuffer = await axios.get(fileUrl, {
    timeout: 5000,
    decompress: true,
    responseType: 'arraybuffer',
  });
}

Though I'm not sure if the exact number of retries could be tested because of race conditions, it tests the fact of retry. I'm so sure because with retries: 1 test fails.