3

I have axios with following HttpClient class

export default class HttpClient {
  constructor(baseUrl: string) {
    const axiosInstance = axios.create({
      validateStatus(status: number) {
        return status === 200 || status === 201;
      },
    });

    axiosInstance.interceptors.request.use((config) => {
      if (AuthUtil.getAuthHeader()) config.headers = AuthUtil.getAuthHeader();
      return config;
    });

    return new Proxy(this, {
      get(_, prop) {
        return (url: string, ...args: any) => {
          url = baseUrl + url;
          return Reflect.get(axiosInstance, prop)(url, ...args);
        };
      },
    });
  }

  get<T = any, R = AxiosResponse<T>>(_url: string, _config?: AxiosRequestConfig): Promise<R> {
    return Promise.resolve(null);
  }
.....
}

Here is the service that uses the HttpClient

export default class UserManagementServiceImpl implements UserManagementService {
  private client = new HttpClient('/api/user');

  async getUser(): Promise<User> {
    const res = await this.client.get('/user');
    return res.data;
  }

I'm testing the service as follows. Here I'm not calling userService.getUser directly but just for testing I created this.

describe('User actions', () => {
  test('creates GET_TERMS_SUCCESS', async () => {
    jest.mock('axios', () => {
      return {
        create: jest.fn().mockReturnValue({
          interceptors: {
            request: { use: jest.fn(), eject: jest.fn() },
            response: { use: jest.fn(), eject: jest.fn() },
          },

          get: jest.fn().mockReturnValue({ data: user }),
        }),
      };
    });

    const user = await userService.getUser();
  });
});

// ERROR:
/*
Error: Error: connect ECONNREFUSED 127.0.0.1:80
*/

I have tried multiple other solutions listed here in stack overflow but does not seem to work. How should this be done?

s1n7ax
  • 2,750
  • 6
  • 24
  • 53
  • I would suggest you take a look at https://www.npmjs.com/package/axios-mock-adapter. This saved me a lot of time spent on mocking and stubbing axios stuff. – MatteoPHRE May 17 '22 at 12:06
  • @MatteoPHRE When you mock axios with axios-mock-adapter, that's only for AxiosStatic instance. Any axios.create() instances are not affected by it. – s1n7ax May 17 '22 at 12:41
  • Not really, i'm using it to mock the AxiosInstance that i'm using inside my class. Basically you only need to expose the axiosInstance from your HttpClient class and pass it to the MockAdapter constructor. Something like: `const mockedHttpClient = new MyHttpClient(faker.internet.url(), {Auth: faker.datatype.string()}); const mockAdapter = new MockAdapter(mockedHttpClient.instance);` (where instance is the axios instance built inside MyHttpClient - here is a singleton) – MatteoPHRE May 17 '22 at 14:32
  • Yeah but things get complex when React component are using the service inside a component. Then I have to export instances of httpclinets from the components which uses any service. As I mentioned I'm only calling the getUser directly in the test just to get it working. – s1n7ax May 17 '22 at 14:55

1 Answers1

7

Update [2022-May-18]

I think I finally figured out how to combine axios-mock-adapter to mock AxiosInstancees created by axios.create().

What we do in testing is, assigning AxiosStatic instance as the return value of axios.create which is accessible locally. So we mock axios as usual using axios-mock-adapter.

import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import UserManagementServiceImpl from '../../../services/impl/UserManagementServiceImpl';

jest.mock('axios', () => {
  return {
    ...(jest.requireActual('axios') as object),
    create: jest.fn().mockReturnValue(jest.requireActual('axios')),
  };
});

const mockAdapter = new MockAdapter(axios);

test('hello', async () => {
  const userService = new UserManagementServiceImpl();

  mockAdapter.onGet('/api/user/user').reply(200, { name: 'name' });
  const r = (await userService.getUser()) as any;
  expect(r.name).toBe('name');
});

Old answer

jest.mock() should always be outside the test life cycle methods apparently.

jest.mock('axios', () => {
  return {
    create: jest.fn().mockReturnValue({
      interceptors: {
        request: { use: jest.fn(), eject: jest.fn() },
        response: { use: jest.fn(), eject: jest.fn() },
      },

      get: jest.fn().mockReturnValue({ data: user }),
    }),
  };
});

describe('User actions', () => {
  test('test', async () => {
    const user = await userService.getUser();
  });
});

You can also mock and define functions later too

jest.mock('axios')

describe('User actions', () => {
  test('test', async () => {
    (axios.create as jest.Mock<any, any>).mockReturnValue({
      interceptors: {
        request: { use: jest.fn(), eject: jest.fn() },
        response: { use: jest.fn(), eject: jest.fn() },
      },

      get: jest.fn().mockReturnValue({ data: { name: 'name' } }),
    });

    const user = await userService.getUser();
  });
});
s1n7ax
  • 2,750
  • 6
  • 24
  • 53