I am trying to unit test a function which sends a post request then the API returns a json object. I am trying to do this using jest and fetch-mock-jest.
Now instead of the expected payload the tested function receives {"size":0,"timeout":0}
and throws error invalid json response body at reason: Unexpected end of JSON input
. Pretty sure there is something basic I don't see. I spent way more time on this without any progress than I'd like to admit.
Edit: I am pretty new to jest and unit testing in general, so if someone has a better suggestion to go about mocking fetch, please tell me.
Test File
import fetchMock from 'fetch-mock-jest'
import {
refreshAccessToken, isTokenExpired
} from '../../../lib/access/AccessToken'
describe('AccessToken Component...', () => {
it('...Refreshes AccessToken', async () => {
const responseBody = { accessToken: taiDeveloperTokenValid } // The payload I want the AccessToken.refreshAccessTokento get
setAccessToken(taiDeveloperTokenExpired)
process.env.NEXT_PUBLIC_AUTH_API_HTTPS_URL = 'http://new-api.com'
fetchMock.post(
`${process.env.NEXT_PUBLIC_AUTH_API_HTTPS_URL}/refreshToken`,
new Promise((res) =>
setTimeout(
() =>
res({
status: 200,
body: JSON.stringify(responseBody),
statusText: 'OK',
headers: { 'Content-Type': 'application/json' },
}),
50,
),
),
)
const refreshAccessTokenResponse = await refreshAccessToken()
expect(refreshAccessTokenResponse).toBe(true)
expect(isTokenExpired()).toBe(false)
})
}
Function I am testing
import fetch from 'isomorphic-unfetch'
export const refreshAccessToken = async (): Promise<boolean> => {
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_AUTH_API_HTTPS_URL}/refreshToken`,
{
method: 'POST',
credentials: 'include',
},
)
console.log(JSON.stringify(await response)) // this prints {"size":0,"timeout":0}
const data = await response.json()
accessToken = data.accessToken
return true
} catch (error) {
console.log(error) // this prints FetchError { message: 'invalid json response body at reason: Unexpected end of JSON input', type: 'invalid-json'
return false
}
}
You can use jest.mock(moduleName, factory, options) to mock isomorphic-unfetch
module by yourself. Don't need fetch-mock-jest
module.
E.g.
main.ts
:
import fetch from 'isomorphic-unfetch';
export const refreshAccessToken = async (): Promise<boolean> => {
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_AUTH_API_HTTPS_URL}/refreshToken`, {
method: 'POST',
credentials: 'include',
});
const data = await response.json();
const accessToken = data.accessToken;
console.log(accessToken);
return true;
} catch (error) {
console.log(error);
return false;
}
};
main.test.ts
:
import { refreshAccessToken } from './main';
import fetch from 'isomorphic-unfetch';
import { mocked } from 'ts-jest/utils';
jest.mock('isomorphic-unfetch');
const fetchMocked = mocked(fetch);
describe('66009798', () => {
afterAll(() => {
jest.resetAllMocks();
});
it('should get access token', async () => {
process.env.NEXT_PUBLIC_AUTH_API_HTTPS_URL = 'http://new-api.com';
const data = { accessToken: '123' };
const mResponse = { json: jest.fn().mockResolvedValueOnce(data) };
fetchMocked.mockResolvedValueOnce(mResponse as any);
const actual = await refreshAccessToken();
expect(actual).toBeTruthy();
expect(fetch).toBeCalledWith('http://new-api.com/refreshToken', {
method: 'POST',
credentials: 'include',
});
expect(mResponse.json).toBeCalledTimes(1);
});
});
unit test result:
PASS examples/66009798/main.test.ts (13.604 s)
66009798
√ should get access token (37 ms)
console.log
123
at examples/66009798/main.ts:11:13
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 83.33 | 100 | 100 | 80 |
main.ts | 83.33 | 100 | 100 | 80 | 14-15
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 15.989 s