Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking node-fetch with jest creating a Response Object for mocking

I am trying to create Response Objects for mocking with jest, I can't seem to get the right syntax.

Initialization,

jest.mock('node-fetch')
const fetch = require('node-fetch')
const { Response, Headers } = jest.requireActual('node-fetch')

// Example adapted from https://fetch.spec.whatwg.org/#example-headers-class
const meta = {
  'Content-Type': 'application/json',
  'Accept': '*/*',
  'Breaking-Bad': '<3'
}
// You can in fact use any iterable objects, like a Map or even another Headers
const headers = new Headers(meta)
const copyOfHeaders = new Headers(headers)

const ResponseInit = {
  status: 200,
  statusText: 'fail',
  headers: headers
}

With a basic test

  test('Basic Test', async () => {
    const token = ''
    const getDocList = new Response(JSON.stringify(downloadDocumentData), ResponseInit)
    fetch.mockResolvedValueOnce(Promise.resolve(getDocList))
    await module.doSomething('mock', token)
      .then( async(res) => {
        await expect(res.data).toEqual(Object)
      })
  }, 5000)

I'm getting an error which is

     FetchError {
        message:
         'invalid json response body at  reason: Unexpected token H in JSON at position 2',
        type: 'invalid-json' }

How can I initial a response for valid json, I have tried a lot of different things.

Following the article at https://jestjs.io/docs/en/bypassing-module-mocks but I want to return and test json instead.

like image 305
GrandFleet Avatar asked Oct 31 '19 17:10

GrandFleet


People also ask

How do you mock fetch in Jest?

We have to mock both promises with jest. fn to get the same behavior: By doing this, when the function getPricesLastDays calls fetch, the mocked out version of fetch will be called. Thus, we have a predictable return.

Can you mock an object in Jest?

When mocking global object methods in Jest, the optimal way to do so is using the jest. spyOn() method. It takes the object and name of the method you want to mock, and returns a mock function. The resulting mock function can then be chained to a mocked implementation or a mocked return value.


1 Answers

We should use jest.mock(moduleName, factory, options) to mock node-fetch module and fetch function.

In order to construct the response object of the fetch function, you need to use the Response class provided by the node-fetch module, so use jest.requireActual(moduleName) to get the original, unmocked node-fetch Module and Response class.

Of course, we can construct the response object arbitrarily, but the instance of the Response class is really close to the real response.

The same goes for headers object.

Here is a working demo:

index.js:

const fetch = require('node-fetch');

module.exports = {
  async doSomething(url, token) {
    return fetch(url).then(res => res.json());
  }
};

index.spec.js:

jest.mock('node-fetch');

const fetch = require('node-fetch');
const { Response, Headers } = jest.requireActual('node-fetch');
const mod = require('./');

const meta = {
  'Content-Type': 'application/json',
  Accept: '*/*',
  'Breaking-Bad': '<3'
};
const headers = new Headers(meta);
const copyOfHeaders = new Headers(headers);

const ResponseInit = {
  status: 200,
  statusText: 'fail',
  headers: headers
};

test('Basic Test', async () => {
  const token = '';
  const downloadDocumentData = { data: {} };
  const getDocList = new Response(JSON.stringify(downloadDocumentData), ResponseInit);
  fetch.mockResolvedValueOnce(Promise.resolve(getDocList));
  const res = await mod.doSomething('mock', token);
  expect(res).toEqual({ data: {} });
  expect(fetch).toBeCalledWith('mock');
});

Unit test result:

 PASS  src/stackoverflow/58648691/index.spec.js
  ✓ Basic Test (5ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.557s

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58648691

like image 188
slideshowp2 Avatar answered Sep 19 '22 04:09

slideshowp2