Here’s a quick note about mocking and testing fetch
calls with Jest.
Let’s take an example React component, ExampleComponent
:
Let’s write a test for it using Jest and Enzyme, ExampleComponent.test.js
:
Some points to note here are:
- By passing the
done
function here, we’re telling Jest to wait until thedone
callback is called before finishing the test. See Testing Asynchronous Code docs for more details. - Since we
await
the call toresponse.json()
inExampleComponent.js
, wePromise.resolve
it in the test to amockSuccessResponse
object. - Since we are
await
ing the call tofetch('https://url-of-your-server.com/example/json')
, inExampleComponent.js
, and expecting the returned object to contain ajson
function, which returns anotherPromise
, wePromise.resolve
to an object containing such ajson
function. - We spy on
window.fetch
and mock it’s implementation to return thePromise
object described in 3. See the section below if you are getting an error like, “Cannot spy the fetch property because it is not a function; undefined given instead”. - We invoke Enzyme to shallow render, which also invokes the React lifecycle methods *.
- Wrapping our assertion code inside a
process.nextTick()
ensures that the functions queued in the current event loop are completed, thus also ensuring that ourPromise
s and other code insideExampleComponent
is done executing. - Optionally, we clear the mock.
- We invoke
done
to tell Jest that this test case is complete.
Note that this was a minimal example for demonstration & education purposes only. There are a lot of things I would do differently if I were writing production-ready code (e.g. abstracting away the data fetching logic, using static typing etc.).
“Cannot spy the fetch property because it is not a function; undefined given instead”
If you get an error, “Cannot spy the fetch property because it is not a function; undefined given instead”, that’s because fetch
has not been polyfill’d in your Jest’s JSDOM environment. As of this writing, there is an open request (jsdom/jsdom#1724) to add fetch
API headers into JSDOM.
So as a workaround, instead of using jest.spyOn
, you can…
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
… and clean it up later using…
global.fetch.mockClear();
delete global.fetch;
* Note about React lifecycle methods and Enzyme v3:
As of Enzyme v3, the
shallow
API does call React lifecycle methods such ascomponentDidMount
andcomponentDidUpdate
.