32

Here's a class I wish to test:

//Request.js
import axios, {AxiosInstance} from 'axios';
import config from './config';

const axiosSingleton: AxiosInstance = axios.create({
  baseURL: 'http://localhost:8080',
});

export default class Request {
  public async get<$ResponseType = any>(url: string): Promise<void> {
    const response = await axiosSingleton.get(url);
    return response.data;
  }
}

when I try testing this by creating a test file, I am not sure how to mock axios. I tried a bunch of ways including - spyOn and automatic mocking. But they don't seem to work. Here's a version of the test file I am not understanding why it doesn't work

// Request.test.js
import axios from 'axios';
import Request from './Request';

interface ITestResponseDataType {
  value: string
}

jest.mock('axios');

describe('Request Tests', () => {
  it('should call axios get with the right relativeUrl', async () => {
    const getMock = jest.fn();

    axios.create.mockReturnValue({
      get: getMock
    });

    getMock.mockResolvedValue({
      value: 'value'
    });

    const data = await new Request().get<ITestResponseDataType>('/testUrl');
    expect(getMock.mock.calls.length).toEqual(1);
    expect(data).toEqual({
      value: 'value'
    });
  });
});

The error I get when I try running the test is -

 TypeScript diagnostics (customize using `[jest-config].globals.ts-jest.diagnostics` option):
    src/common/api/Request.test.ts:15:18 - error TS2339: Property 'mockReturnValue' does not exist on type '(config?: AxiosRequestConfig | undefined) => AxiosInstance'.

    15     axios.create.mockReturnValue({

This error makes sense, because the type defined in axios for axios.create should not allow .mockReturnValue to be called on .create. So how do I tell typescript that jest has gone in and modified it?

frederj
  • 1,323
  • 8
  • 20
Ashwini Raman
  • 786
  • 1
  • 6
  • 11
  • I got the same error and stumbled over here... it turned out for my mocked function I wasnt actually invoking jest.fn() – frederj Apr 07 '21 at 19:31

4 Answers4

69

Cast the mock method to jest.Mock, ie

import axios from "axios"
import Request from "./Request";

// Create an Axios mock
// Don't worry about the order, Jest will hoist this above the imports
// See https://jestjs.io/docs/manual-mocks#using-with-es-module-imports
jest.mock("axios", () => ({
  create: jest.fn()
}))

// Customise the `create` mock method
(axios.create as jest.Mock).mockReturnValue({
  get: getMock
})
Phil
  • 141,914
  • 21
  • 225
  • 223
  • This doesn't work for when the module / file being tested is calling the "Mocked" function globally, like in @ashwini 's case `const axiosSingleton: AxiosInstance = axios.create(...)`. Moving the AxiosInstance to a local variable in the functions works, but now you're not using the singleton. Any other ideas? – Dan B Jun 28 '20 at 23:12
  • @DanB When you use `jest.mock`, it will hoist the mock instance above any imports so it will also be used in the production files during testing – Phil Mar 25 '21 at 21:48
7

Just an addition to top rated answer. I would prefer maintaining type definitions when type casting. That can be be rewritten as

(axios as jest.Mocked<typeof axios>).create.mockReturnValue({
  get: getMock
});
Amit Kumar
  • 71
  • 2
  • 3
  • 1
    Didn't work for me: ``` Argument of type '{ get: jest.Mock; }' is not assignable to parameter of type 'AxiosInstance'. Type '{ get: Mock; }' is missing the following properties from type 'AxiosInstance': defaults, interceptors, getUri, request, and 6 more.ts(2345) ``` – Dan B Jun 28 '20 at 21:21
1

You need to replace the axios.create method with a Jest mock function:

axios.create = jest.fn();

That should allow you to then set its return value.

Glitcher
  • 1,068
  • 6
  • 14
1

I solved this with axios-mock-adapter, works for me without any problem and also helps with nested calls.

    // src/request.ts
    import axios from "axios";
    export const axiosCreate = axios.create();
    export async function someRequest(){
      axiosCreate.get("http://localhost/users");
    }

    // src/__test__/request.test.ts
    import * as request from ".../request";
    import MockAdapter from "axios-mock-adapter";
    const mock = new MockAdapter(request.axiosCreate);
    it("Testing mock", async () => {
      mock.onGet("http://locahost/users").reply(200, "ok");
      const resp = await request.someRequest();
      expect(resp).toEqual("ok");
    });

Hopefully can help to someone.