javascripttypescriptunit-testingjestjsfetch-mock

fetch-mock-jest .post resolves to {"size":0,"timeout":0}


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
  }
}


Solution

  • 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