Unit test interceptor logic in Axios

Viewed 2133

I have an interceptor set up in Axios that will refresh tokens and then retry the call if the initial HTTP response is a 401.

I'm trying to unit test this logic by mocking Axios request to return a 401 and then a valid response (see a simplified version below) and asserting that it gets called twice.

My problem is that because I'm mocking request the interceptor doesn't seem to run. Rather than retry the request, I get the stubAuthError returned.

Is there any way of achieving what I'm after, or is there a better way to test this?

index.js

const axios = require('axios');

axios.interceptors.response.use(
  response => response,
  error => {
    const status = error.response ? error.response.status : null;

    if (status === 401) {
      return makeRequest();
    } else {
      return Promise.reject(error);
    }
  }
);

async function makeRequest() {
  return axios.request('url');
}

module.exports = {
  makeRequest
};

index.spec.js

const axios = require('axios');

const { makeRequest } = require('./index');

const stubAuthError = { response: { status: 401 } };
const stubResponse = { data: { foo: 'bar' } };

it('should make the request twice', async () => {
  expect.assertions(1);

  axios.request = jest.fn();
  axios.request
    .mockRejectedValueOnce(stubAuthError)
    .mockResolvedValueOnce(stubResponse);

  try {
    await makeRequest();
  } catch (error) {}

  expect(axios.request).toHaveBeenCalledTimes(2);
});
1 Answers

interceptors are registered sequentially, similar to express middleware,

you can access axios.interceptors.request and axios.interceptors.response to unit test functions registered on the interceptor manager

for example:

on my main.js:

let axios = require('axios');

axios.interceptors.response.use(
  response => response,
  error => {
    const status = error.response ? error.response.status : null;

    if (status === 401) {
      return makeRequest();
    } else {
      return Promise.reject(error);
    }
  }
);

async function makeRequest() {
  return axios.request('https://randomuser.me/api/');
}

module.exports = {
  makeRequest
};

on my main.test.js:

let axios = require('axios');

// 
// based on your question, we don't want to test "makeRequest"
// so, I'm loading the file only to register the interceptors
//
require('./main');

let stubAuthError = { response: { status: 401 } };
let stubExceptionError = { response: { status: 500 } };
let stubResponse = { data: { foo: 'bar' } };

test("interceptor fulfilled", () => {
  let res = axios.interceptors.response.handlers[0].fulfilled(stubResponse);
  expect(res).toEqual(stubResponse);
});

test("interceptor rejected => makeRequest", () => {
  let originalAxiosRequest = axios.request;

  // this fn will be called by "makeRequest"
  axios.request = jest.fn();

  axios.interceptors.response.handlers[0].rejected(stubAuthError);

  expect(axios.request).toHaveBeenCalledTimes(1);

  axios.request = originalAxiosRequest;
});

test("interceptor rejected => exception", async () => {
  await expect(axios.interceptors.response.handlers[0].rejected(stubExceptionError)).rejects.toEqual(stubExceptionError)
});
Related