1

Trying to mock an Axios call to unit test a token response from our identity software. Axios is not getting called at all and because of that my return is always undefined.

I've tried changing up Axios call to axios.post and changing the way I've mocked this more times then I can count. I don't believe like I should have to install another mocking framework just for Axios to mock this one function.

Implementation:

async getAuthToken() {        
    const oauthUrl = process.env.OAUTHURL;
    const oauthAudience = process.env.OAUTHAudience;
    const oauthUsername = process.env.OAUTHUSERNAME;
    const oauthPassword = process.env.OAUTHPASSWORD;
    
    let urlForAuth = oauthUrl
    urlForAuth = urlForAuth + '/as/token.oauth2?';
    urlForAuth = urlForAuth + 'grant_type=client_credentials';
    urlForAuth = urlForAuth + '&aud=' + oauthAudience + '/';
    urlForAuth = urlForAuth + '&scope=' + oauthAudience + '/.read';
    
    const options = {
        method: 'POST',
        url: urlForAuth,
        headers: {
            'Authorization': "Basic " + Buffer.from(oauthUsername + ":" + oauthPassword).toString("base64")
        },
        responseType: 'json'
    };
    
    try{
        let response = await axios(options); 
        return response.data.access_token;
    }
    catch(e){
        console.log(e);
        throw e;
    }       
}

Test Case:

test('token Is Returned', async () => {
    expect.hasAssertions();

    let Response = `{
            "access_token": "thisisatoken",
            "token_type": "Bearer",
            "expires_in": 3599
        }`;

    axios = jest.fn(() => Promise.resolve());

    axios.mockImplementationOnce(() =>
        Promise.resolve({
            data: Response
        })
    );            
    
    let response = await AuthService.getAuthToken();

    expect(axios).toHaveBeenCalledTimes(1);
    expect(response).toEqual("thisisatoken");  
});

The error I am getting is

Expected mock function to have been called one time, but it was called zero times.

When I debug the data element on the response contains the following:

data:"Copyright (c) 2019 Next Small Things\n"

That is no where in my code. help.

greybeard
  • 2,249
  • 8
  • 30
  • 66
mrcavanaugh09
  • 333
  • 1
  • 3
  • 14
  • You need to know why this happened `Expected mock function to have been called one time, but it was called zero times.` it is because you expect to `jest.fn()` have been called for 1 times `expect( jest.fn() ).toHaveBeenCalledTimes( 1 )` but that function never called. So please read the documentation here https://jestjs.io/docs/en/expect#tohavebeencalledtimesnumber – Eka putra Aug 01 '19 at 03:41
  • 1
    @Ekaputra I believe author understands that. And asks us what could be a reason mock has not been called. – skyboyer Aug 01 '19 at 06:13

1 Answers1

2

You cannot mock things this way. Actually you are mocking axios only for your test's code but not for component under test that import's axios on its own.

You need to mock module properly and you have actually plenty of options:

  1. provide ready-to-use mock in __mocks__ folder
  2. call jest.mock('axios') to get autogenerated mock(each exported member will become jest.fn automatically)
  3. provide factory for mock jest.mock('axios', () => { .... return object like it all are exported from file ... })

Also you need to import axios into your test to access it:

import axios from 'axios';

jest.mock('axios');

test('token Is Returned', async () => {
    expect.hasAssertions();

    let Response = `{
            "access_token": "thisisatoken",
            "token_type": "Bearer",
            "expires_in": 3599
        }`;

    axios.mockReturnValue(() =>
        Promise.resolve({
            data: Response
        })
    );            

    let response = await AuthService.getAuthToken();

    expect(axios).toHaveBeenCalledTimes(1);
    expect(response).toEqual("thisisatoken");  
});

Beware of few things:

  1. jest.mock is hoisted to the top even if it's declared somewhere really deep in test's code. So it's better to place all jest.mock at the top of the file - because it anyway works this way - and this way another developer would not be confused if it's mocked or not.
  2. if using __mocks__ folder with auto mock you better inject jest.fn() in advance - most times you will like to check if part of mock has been called and with what arguments
  3. jest.mock cannot refer to any sibling variable except those one with name starting from mock.... See Service mocked with Jest causes "The module factory of jest.mock() is not allowed to reference any out-of-scope variables" error with really good explanation.
  4. it'd be hard(near to impossible) to unmock module partially. so consider your test either mock some module or does not mock at all to test it.
skyboyer
  • 22,209
  • 7
  • 57
  • 64
  • While the code block here didn't fix my problem, the information you posted led me to completely change my implementation and obtain what i was looking for. Thanks. – mrcavanaugh09 Aug 02 '19 at 23:09
  • surprisingly :) maybe you could compose own answer and mark it as appropriate one? this could help someone else – skyboyer Aug 03 '19 at 06:27