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.
[upd] this answer is 5 years old. Consider using holistic network mocking - e.g. msw
or nock
. Unlike with module-level mocking it's way more flexible, lib- and framework-agnostic(e.g. you may have app-calls made with axios
and some package requesting through low-level fetch()
) and even more readable
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:
__mocks__
folderjest.mock('axios')
to get autogenerated mock(each export
ed member will become jest.fn
automatically)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:
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.__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 argumentsjest.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.