Source:  Twitter logo

I have the following method in a class:

import axios from 'axios'

public async getData() {
   const resp = await axios.get(Endpoints.DATA.URL)
   return resp.data
}

Then I am trying to set up a Jest test that does this:

jest.mock('axios')

it('make api call to get data', () => {
   component.getData()
   expect(axios.get).toHaveBeenCalledWith(Endpoints.DATA.URL)
})

The problem is that because I am not mocking the return value, then it gives an error for resp.data because I'm calling data on null or undefined object. I spent at least 2 hours trying various ways to get this working but I can't find a way such that I can mock axios.get with some return value.

Jest's documentation uses JavaScript so they give this example axios.get.mockResolvedValue(resp) but I can't call mockResolvedValue because that method does not exist on axios.get in TypeScript.

Also, if you know other good testing library for React other than Jest that does this stuff easily for TypeScript, feel free to share.

In start of file:

import axios from 'axios';
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

Now you can use it as usual mock:

mockedAxios.get.mockRejectedValue('Network error: Something went wrong');
mockedAxios.get.mockResolvedValue({ data: {} });
97 users liked answer #0dislike answer #097
Jan Tumanov profile pic
Jan Tumanov

If you want to use jest.mock with "no-any" try this:

import axios, { AxiosStatic } from 'axios'

interface AxiosMock extends AxiosStatic {
  mockResolvedValue: Function
  mockRejectedValue: Function
}

jest.mock('axios')
const mockAxios = axios as AxiosMock

it('make api call to get data', () => {
   // call this first
   mockAxios.mockResolvedValue(yourValue)

   component.getData()
   expect(mockAxios.get).toHaveBeenCalledWith(Endpoints.DATA.URL)
})
17 users liked answer #1dislike answer #117
Adam T. Smith profile pic
Adam T. Smith

This is what I personally always use.

import axios from 'axios';
jest.mock('axios')

it('...', () => {
  (axios.get as jest.Mock).mockImplementationOnce(() => Promise.resolve({}));
  // test here
  expect(axios.get).toHaveBeenCalled()
}
6 users liked answer #2dislike answer #26
Ramsy de Vos profile pic
Ramsy de Vos

I kept running into is not a function issues. If the accepted answer doesn't work for you, then try importing axios with a capital A ie. Axios.

import Axios from 'axios';
jest.mock('Axios');
const mockedAxios = Axios as jest.Mocked<typeof Axios>;
4 users liked answer #3dislike answer #34
Rene Juuse profile pic
Rene Juuse

but I can't call mockResolvedValue because that method does not exist on axios.get in TypeScript

You can use an assertion:

(axios.get as any).mockResolvedValue(resp)
3 users liked answer #4dislike answer #43
basarat profile pic
basarat

As of Jest 24.9.0 here is how it works correctly typing both axios and Jest properties.

What we would like for a typed mock is that the mocked object type contains the union of the mocked object type and the type of Jest mocks. As far as I seen non of the current answers enable that.

jest.MockedFunction

jest.MockedClass

import axios from 'axios';
jest.mock('axios');

const mockedAxios = axios as jest.MockedFunction<typeof axios>;

mockedAxios.mockResolvedValue({ status: 200, data: 'mockResponse' });

// Or:
(mockedAxios.get as jest.MockedFunction<typeof mockedAxios.get>).mockResolvedValue('mockResponse');

As you can see, you can either manually cast what you need or you'll need something to traverse all axios properties/methods to type everything.

To do that (deep mock types) you can use jest.mocked() introduced in Jest 27.4.0

import axios from 'axios';
jest.mock('axios');

const mockedAxios = jest.mocked(axios, true); 

mockedAxios.mockImplementation() // correctly typed
mockedAxios.get.mockImplementation() // also correctly typed
3 users liked answer #5dislike answer #53
Rui Marques profile pic
Rui Marques

I found a neat solution using the sinon library npm install sinon @types/sinon --save-dev.

Then the testing code becomes:

let component: Component
let axiosStub: SinonStub

beforeAll(() => {
    component = new Component({})
    axiosStub = sinon.stub(axios, 'get')
})

afterAll(() => {
    axiosStub.restore()
})

it('make api call to get data', async () => {
    // set up behavior
    axiosStub.withArgs(Endpoints.DATA.URL).returns({data: []})

    // method under test
    const res = await component.getData()

    // assertions
    expect(res).toEqual([])
})
2 users liked answer #6dislike answer #62
Stefan Zhelyazkov profile pic
Stefan Zhelyazkov

Another option is to use jest.spyOn:

import axios from "axios";

jest.spyOn(axios, "get").mockImplementation(() => Promise.resolve({data: []}));

This also gives you the benefit of having a mocked method that you can test against, for example:

import axios from "axios";

// ...

const mockedGet = jest
  .spyOn(axios, "get")
  .mockImplementation(() => Promise.resolve({data: []}));

// ...

expect(mockedGet).toBeCalledWith('https://example.api?q=abc&k=123');
0 users liked answer #7dislike answer #70
MauricioRobayo profile pic
MauricioRobayo

Copyright © 2022 QueryThreads

All content on Query Threads is licensed under the Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0).