Reputation: 343
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
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