Open twoneks opened 6 years ago
Can't immediately see what's going, but it doesn't look like you have a mock inside your test file:
describe('request', () => {
beforeEach(() => {
fetch.resetMocks();
});
it('perform the fetch with the default headers', () => {
const defaultHeaders = {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type',
}
//Shouldn't there be a mock here if you're resetting it every time?
fetch.once(/* args */ )
request('http://fake_url', {})
expect(fetch.mock.calls.length).toBe(1);
expect(fetch.mock.calls[0][1].headers).toEqual(defaultHeaders);
});
Actually in this particular test I don't want to mock a response but just check the header and that the fetch call has been performed. I share the entire file, maybe it will make more sense.
/**
* Test request
*/
import request from '../request';
describe('request', () => {
// beforeEach(() => {
// fetch.resetMocks()
// });
it('perform the fetch with the default param', () => {
const defaultHeaders = {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type',
};
request('http://fake_url', {});
expect(fetch.mock.calls.length).toBe(1);
expect(fetch.mock.calls[0][1].headers).toEqual(defaultHeaders);
});
describe('allow to customize headers', () => {
it('merge new headers without deleting default values', () => {
const customHeaders = {
CustomParam: 'CustomValue',
};
request('http://fake_url', customHeaders);
expect(fetch.mock.calls[1][1].headers).toEqual({
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type',
CustomParam: 'CustomValue',
});
});
it('correctly override the defautl values', () => {
const customHeaders = {
CustomParam: 'CustomValue',
'Access-Control-Allow-Origin': '0.0.0.0',
};
request('http://fake_url', customHeaders);
expect(fetch.mock.calls[2][1].headers).toEqual({
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '0.0.0.0',
'Access-Control-Allow-Headers': 'Content-Type',
CustomParam: 'CustomValue',
});
});
});
describe('allow to customize method and body', () => {
it('merge new headers without deleting default values', () => {
request('http://fake_url', {}, 'POST', { id: '1234567890' });
expect(fetch.mock.calls[3][1].method).toEqual('POST');
expect(fetch.mock.calls[3][1].body).toEqual('{"id":"1234567890"}');
});
});
it('return a parsed JSON', () => {
fetch.mockResponseOnce(JSON.stringify({ data: '12345' }));
request('http://fake_url', {}).then(res => {
expect(res.data).toEqual('12345');
});
});
it('return null on code from 204 to 205', () => {
fetch.mockResponseOnce(JSON.stringify({}), { status: 204 });
request('http://fake_url', {}).then(res => {
expect(res.data).toEqual(null);
});
});
it('handle erorrs', () => {
let responseError;
const expectedError = new Error('Internal Server Error');
fetch.mockResponseOnce(JSON.stringify({}), {
status: 500,
});
Promise.all([
request('http://fake_url', {}).catch(err => {
responseError = err;
expect(responseError).toEqual(expectedError);
}),
]);
});
});
The first 4 tests are meant to test the parameters. I don't care about the response. As you can see in each test I have to increment the index in the fetch.mock.calls[0][1]
and this is creating a dependency between one test and another. If they are not executed in this sequence they will fail. Moreover I would like to add in each test the assertion expect(fetch.mock.calls.length).toBe(1);
but doing so it will fail because in the second test will be expect(fetch.mock.calls.length).toBe(2);
ans so on creating a dependency between test execution. I thought the beforeEach(() => {fetch.resetMocks();});
is meant to be used to reset the calls made over fetch. Am I wrong?
The same problem with you! Here is my code:
get(url, data = {}) {
const path = new URL(url)
path.search = new URLSearchParams(data)
return fetch(path).then(resp =>
new Promise((resolve) =>
resp.json()
.then(data => resolve(data))
.catch(() => resolve(resp.text()))
)
)
jest.setup.js
require('jest-localstorage-mock')
const { JSDOM } = require('jsdom')
const dom = new JSDOM(
'<!DOCTYPE html><html><body></body></html>',
{
runScripts: 'dangerously'
})
global.window = dom.window
global.document = dom.window.document
global.fetch = require('jest-fetch-mock')
fetch is inside my test case and throw an error:
TypeError: Cannot read property 'then' of undefined
3 | const path = new URL(url)
4 | path.search = new URLSearchParams(data)
> 5 | return global.fetch(path).then(resp =>
| ^
6 | new Promise((resolve) =>
7 | resp.json()
8 | .then(data => resolve(data))
But it will be successful with the following:
get(url, data = {}) {
const path = new URL(url)
path.search = new URLSearchParams(data)
return window.fetch(path).then(resp => resp.text()).then(text => {
let result
try {
result = JSON.parse(text)
} catch (e) {
result = text
}
return result
})
}
update:
When I use this in my code, it works:
global.fetch.mockResponse([
JSON.stringify({ 'perf': 'data' })
], [
'@@ -0,0 +1,14 @@\n+console.log(1);'
])
But two global.fetch.once('@@ -0,0 +1,14 @@\n+console.log(1);')
Will Be Error
I'm having exactly the same problem. After running fetch.resetMocks()
the fetch()
call returns undefined
instead of a promise.
What could be wrong?
@slavafomin fetch.resetMocks()
calls jest's mockReset function, which leads to those undefined
values. It's a feature, not a bug 😎
Regarding original problem, I managed to solve it by the following change:
// jest.config.js
module.exports = {
...
- resetMocks: true,
+ clearMocks: true,
...
};
any updates or workarounds?
It seems like mocked default implementation has been cleaned up due to Jest's mockReset.
I've managed similar issue by adding following workaround:
beforeEach(() => {
fetch.resetMocks();
fetch.mockResponse('');
});
TLDR: Async code was causing the wrong test to throw the error. Check the solution to see what I mean.
Original: None of these suggestions worked for me. And the problem is it's inconsistent. Using Create-react-app and React Testing Library. My code looks something like this:
//test.js
global.fetch = require('jest-fetch-mock')
afterEach( ()=> {
fetch.resetMocks()
//clear other mocks
}
test(<MyComponent/>, () => {
const data = {};
fetch.once(JSON.stringify(data));
render(<MyComponent />)
} )
and the actual code on the page
componentDidMount(){
fetch('/data')
.then(res => res.json())
.then(data => useData(data));
}
I have 14 other tests that rely on the exact same code in this file, loading the same component, executing the same fetch which returns the same data and they all work properly. But this last test just keeps throwing the FetchError: invalid json response body.
The same function on the page is calling the fetch, and handling it the exact same way as in all the other tests - it's identical. I even checked the stringified responses returned from the mocked fetch and they seem identical between when it works and when it doesn't;
Solution:
I found the issue in my particular file. In the test above the failing one, which was seemingly passing I had
fetch.resetMocks():
fetch.once(JSON.stringify()) // undefined
Replacing that with a JSON.stringify({ })
solved it. Why? No idea. Because it's async code. I never needed to check the actual response in the test above the failing one, so I wasn't waiting for it. But it was erroring out, only doing so in async manner, leading Jest and me to think the next test was the one throwing errors.
Waiting on the response
fetch.resetMocks():
fetch.once(JSON.stringify()) // undefined
await wait();
resulted in the error being thrown/caught in the correct test which was until now seemingly passing. And using JSON.stringify({})
worked because it removed the actual error.
@slavafomin
fetch.resetMocks()
calls jest's mockReset function, which leads to thoseundefined
values. It's a feature, not a bug 😎
How to get around that "feature"?
const res = await fetch(url, options);
if (!res.ok) { // here res is undefined
Anyone found a solution for this?
Hi! I have a problem using the fetch.resetMocks(); in the before each statement.
Executing the test with or without fetch.resetMocks(); affects the response of the test.
Without it works well. With I get
I want to execute more than one test over fetch in this file so I really need to reset it. Great project by the way. Cheers.