user3693309
user3693309

Reputation: 343

Mock fetch within a function in Typescript

I am new to typescript and apologize in advance if this is trivial. I have a function that uses Fetch internally like so

export class MyClass {
  classVar1: string;
  classVar2: string;
...
  async postEvent(event: eventSchema) {
    
    const headers = {
      'Content-type': 'application/json',
      'Api-Key': 'APIKEY}',
    };

    let result = null;

    const response = await fetch('url', {
        method: 'POST',
        body: event,
        headers: headers,
      });
    result = await response;
    
    return result;
  }
}

I am trying to test the postEvent method and for that I am trying to mock the fetch portion ONLY. But no matter how I approach this, still the actual method is the one that gets called. Based on what I found online, this is what I have in my .spec.ts file

import { MyClass } from './my-class';
import fetch from 'node-fetch';

jest.mock('node-fetch');
describe('My class mocked fetch', () => {
  beforeEach(() => {
    (MyClass as any).postEvent = jest.fn();
    (fetch as any) = jest.fn().mockResolvedValue({
      url: 'url',
      status: 200,
      statusText: 'OK',
      counter: 0,
    });
  });

  it('Calls mockedFetch and gets the mocked values', async () => {
    expect(fetch).toHaveBeenCalledTimes(0);
    const response = await myClass.postEvent('testMsg')
    console.log('RESPONSE', response)
  });

In the last line (console.log('RESPONSE', response)), I expect to see the mockResolved values, i.e

url: 'url',
  status: 200,
  statusText: 'OK',
  counter: 0,

Yet it returns whatever the actual fetch would generate.

Can anyone help with this?

p.s I have also read about the spyOn approach, yet I am not able to find an example for typescript that would fit my use case above. There is an example for JS like this

const fetchMock = jest.spyOn(global, 'fetch').mockImplementation(() => Promise.resolve({ json: () => Promise.resolve([]) }))

But it throws an exception if used in type script like below

'Promise<{ json: () => Promise<any[]>; }>' is not assignable to type 'Promise<Response>'.

Upvotes: 3

Views: 7671

Answers (1)

Lin Du
Lin Du

Reputation: 102587

There are some problems with your test, I will explain them one by one.

You want to test the postEvent method of MyClass, then you should NOT replace it with a mock function created by jest.fn().

If the fetch function comes from node-fetch, then you should mock node-fetch module rather than the global.fetch.

After we mocked node-fetch using jest.mock('node-fetch'), jest will automatically create mocked objects for each member exposed by node-fetch.

node-fetch exports Response class, we can use it to create a mock response. But as mentioned earlier, jest.mock() has created a mocked version of the Response class, how do we get the original Response class?

jest.requireActual(moduleName) can help us, it returns the actual module instead of a mock.

In summary, the following is the complete test code:

my-class.ts:

import fetch from 'node-fetch';

export class MyClass {
  async postEvent(event) {
    const headers = {
      'Content-type': 'application/json',
      'Api-Key': 'APIKEY',
    };

    const response = await fetch('url', {
      method: 'POST',
      body: event,
      headers: headers,
    });
    return response.json();
  }
}

index.test.ts:

import { MyClass } from './my-class';
import fetch from 'node-fetch';

jest.mock('node-fetch');
const { Response } = jest.requireActual('node-fetch');

describe('69579937', () => {
  it('Calls mockedFetch and gets the mocked values', async () => {
    expect(fetch).toHaveBeenCalledTimes(0);

    (fetch as jest.MockedFunction<typeof fetch>).mockResolvedValue(
      new Response(JSON.stringify({ name: 'teresa teng' }), { url: 'url', status: 200, statusText: 'OK' })
    );
    const myclass = new MyClass();
    const response = await myclass.postEvent('testMsg');
    expect(response).toEqual({ name: 'teresa teng' });
  });
});

test result:

 PASS  examples/69579937/my-class.test.ts
  69579937
    ✓ Calls mockedFetch and gets the mocked values (3 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.893 s, estimated 11 s
Ran all test suites related to changed files.

Upvotes: 2

Related Questions