0

I'm testing a component that calls an API to populate a table with data. Though axios is used, axios is being wrapped in a convenience method of sorts to populate headers before executing the request via interceptors. I've tried axios-mock-adapter, but it's not working. I'm still new to testing React and I'm lost on how to mock data coming back from the api/axios. How do I go about mocking the api call to mock the data for my tests to pass??

This is my simple test:

test('<EmailTable/> ', async () => {
  const { debug, getByText } = render(<CommunicationEmail />);
  await waitFor(() => expect(getByText('Test Email Subject')).toBeTruthy());
}

This is the axios wrapper (api.js):

const instance = axios.create({
  baseURL: `${apiUrl}/v1`,
  timeout: 12000,
  withCredentials: true,
  headers: headers,
});

//intercept requests to validate hashed auth token
instance.interceptors.request.use((request) => {
  const token = request.headers['X-Our-Access-Token'];
  if (
    localStorage.getItem('user_token') == null ||
    SHA256(token).toString(enc.Hex) == localStorage.getItem('user_token')
  ) {
    return request;
  } else {
    console.log({ what: 'Auth key invalid' });
    return Promise.reject('Invalid token!');
  }
});

//intercept responses to handle 401 errors
instance.interceptors.response.use(
  (response) => {
    return response;
  },
  (error) => {
    // handle 401 authentication errors and redirect to SSO
    if (error.response != null && error.response.status != null && error.response.status === 401) {
      console.error({ what: 'Authorization error', e: error });
    }
    return Promise.reject(error);
  }
);

export default instance;

And here's a simplification of the component I'm trying to test:

import api from './api.js';

const EmailTable = () => {
   const [emails, setEmails] = useState();

   useEffect(() => {
      if(!emails) {
         getEmails();
      }
   }, [emails]);

   const getEmails = async () => { 
      await api({
        method: 'GET',
        url: `/communications/emails`,
      }).then((response) => {
        if (response.success) {
           setEmails(response.emails);
        }
      }
    }

   if(!emails) { return <div> Loading... </div> }; 
   return <div>{emails}</div>;
}

UPDATE WITH SOLUTION:

To mock the axios wrapper that is my API, I had to mock the api module and return a resolved promise like so:

jest.mock('../api', () => {
  return function (request) {
    // If we want to mock out responses to multiple API requests, we could do if (request.url = "/blah/blah") { return new Promise.... }
    return new Promise((resolve) => {
      resolve({
        data: { success: true, emails: [] },
      });
    });
  };
});
L Becker
  • 503
  • 5
  • 16
  • 1
    Why not mock out the facade? Then you're not coupling to Axios. – jonrsharpe Sep 02 '20 at 21:16
  • I'm not sure how to do that. The "facade" is returning an axios instance with those interceptors. I've tried doing jest.mock('./api', () => { return data; }) but then I get an error: TypeError: (0 , _Api.default) is not a function. – L Becker Sep 02 '20 at 21:28
  • You should re-read the docs on Jest's mocking, the callback function should return the *implementation*: https://jestjs.io/docs/en/es6-class-mocks#mock-using-module-factory-parameter – jonrsharpe Sep 02 '20 at 21:32

0 Answers0