Search code examples
javascriptunit-testingaxiosjestjsts-jest

2 different instance of axios mock having returning an error of axios.default is not a function


In my function named eventController. An axios call has implemented in two ways The 1st one is on this code

axios.post(
           `${url}/API1`,
           {
              data:'data for API1'
           }
        )

Then this is the 2nd

 await axios({
     method: 'POST',
     url: `${url}/API2`,
     data: {
         data:'data for API2'
     }
  })

2 different axios calls in one function/controller

I'm assigned in implementing a unit test for this using jest in typescript

So what I did so far is this

jest.mock('axios', () => ({
   post: jest
      .fn()
      .mockResolvedValueOnce({ status: 200 }) 
}));

    it("should work", async () => {
      const req = {body: {
     data: 'data for API'
}}
      const response = await eventController(req);

      expect(response.status).toBe(200);
   });

I mock the 1st axios call with .post method but when doing it w/ the second one using the default axios module I have received this error

  TypeError: (0 , axios_1.default) is not a function 

I've tried to implement this way also but both don't seem to work This:

    jest.mock('axios', () => ({
   __esModule: true,
   default: jest.fn(),
   post: jest
      .fn()
      .mockResolvedValueOnce({ status: 200 }) 
}));

and this:

jest.mock('axios', () => ({
           __esModule: true,
           default: { 
post: jest
              .fn()
              .mockResolvedValueOnce({ status: 200 })
    },
          
        }));

Hoping someone can help me on this one. This is the versions of the library I'm currently using

  • "jest": "^27.0.6"
  • babel-jest": "^27.0.6"
  • "axios": "^0.21.4",
  • @types/jest": "^27.0.0",
  • "ts-jest": "^27.0.4",

Solution

  • Just use jest.mock(moduleName, factory, options) and without passing the factory argument, let the jest mocks a module with an auto-mocked version. And, there is no __mocks__ directory.

    You need to handle the TS types for axios function and axios.get() method after mocking, use type cast to do this.

    Note: I use a simple string as the mock resolved value which does not match the AxiosResponse interface.

    E.g.

    main.ts:

    import axios from 'axios';
    
    const url = 'http://localhost:3000';
    export async function main() {
      const res1 = await axios.post(`${url}/API1`, {
        data: 'data for API1',
      });
      console.log('res1: ', res1);
      const res2 = await axios({
        method: 'POST',
        url: `${url}/API2`,
        data: {
          data: 'data for API2',
        },
      });
      console.log('res2: ', res2);
    }
    

    main.test.ts:

    import { main } from './main';
    import axios from 'axios';
    
    jest.mock('axios');
    
    const axiosMock = axios as jest.MockedFunction<typeof axios>;
    const axiosPostMock = axios.post as jest.MockedFunction<typeof axios.post>;
    
    test('should pass', async () => {
        expect(jest.isMockFunction(axios)).toBe(true);
        expect(jest.isMockFunction(axios.post)).toBe(true);
        axiosPostMock.mockResolvedValueOnce('fake data 1');
        axiosMock.mockResolvedValueOnce('fake data 2' as any);
        await main();
    });
    

    Test result:

     PASS  stackoverflow/76407602/main.test.ts (38.236 s)
      ✓ should pass (48 ms)
    
      console.log
        res1:  fake data 1
    
          at stackoverflow/76407602/main.ts:8:11
    
      console.log
        res2:  fake data 2
    
          at stackoverflow/76407602/main.ts:16:11
    
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        41.384 s