Cheatsheet
Installation
npm i -D fetch-mock
(or npm i -D fetch-mock-jest
if using jest)
Global fetch
import/require the fetch-mock/fetch-mock-jest library. For the vast majority of test toolchains this should just work without any additional wiring.
Local fetch with jest
jest.mock('node-fetch', () => require('fetch-mock-jest').sandbox());
const fetchMock = require('node-fetch');
Local fetch with other test runners
// pass this mock into your module mocking tool of choice
// Example uses https://www.npmjs.com/package/proxyquire
const fetchMock = require('fetch-mock').sandbox();
proxyquire('./my-module', { 'node-fetch': fetchMock });
Setup and teardown
Mock setup methods
All these methods can be chained e.g. fetchMock.getAny(200).catch(400)
- Stub fetch and define a route
.mock(matcher, response)
- Stub fetch without a route
.mock()
- Spy on calls, letting them fall through to the network
.spy()
- Let specific calls fall through to the network
.spy(matcher)
- Respond with the given response to any unmatched calls
.catch(response)
- Add a mock that only responds once
.once(matcher, response)
- Add a mock that responds to any request
.any(response)
- Add a mock that responds to any request, but only once
.anyOnce(response)
- Add a mock that only responds to the given method
.get()
,.post()
,.put()
,.delete()
,.head()
,.patch()
- Combinations of the above behaviours
getAny()
,getOnce()
,getAnyOnce()
,postAny()
...
Tear down methods
- Remove all mocks and history
.restore()
,.reset()
- Discard all recorded calls, but keep defined routes
.resetHistory()
- Discard all routes, but keep defined recorded calls
.resetBehavior()
Request matching
The following request would be matched by all the mocks described below:
fetch('http://example.com/users/bob?q=rita', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: '{"prop1": "val1", "prop2": "val2"}',
});
Urls
Can be passed as
- the first argument
.mock('blah', 200)
- a
url
property on the first argument.mock({url: 'blah'}, 200)
Patterns
- Match any url
'*'
- Match exact url
'http://example.com/users/bob?q=rita'
- Match beginning
'begin:http://example.com'
- Match end
'end:bob?q=rita'
- Match path
'path:/users/bob'
- Match a glob expression
'glob:http://example.{com,gov}/*'
- Match express syntax
'express:/users/:name'
- Match a RegExp
/\/users\/.*/
Naming routes
When defining multiple mocks on the same underlying url (e.g. differing only on headers), set a name
property on the matcher of each route.
Other properties
The following should be passed as properties of the first argument of .mock()
.
- Match the request method
{method: 'POST'}
- Match headers
{headers: {'Content-Type': 'application/json'}}
- Match a JSON body
{body: {prop1: 'val1', prop2: 'val2'}}
- Match part of a JSON body
{body: {prop1: 'val1'}, matchPartialBody: true}
- Match query parameters
{query: {q: 'rita'}}
- Match express path parameters
{url: 'express:/users/:name', params: {name: 'bob'}}
Custom
Match on any condition you like by:
- using a function
{functionMatcher: (url, options, request) => url.length > 100}
(or can just pass the function in as the first parameter, not wrapped in an object) - defining your own declarative matchers with
addMatcher()
, e.g. setting up declarative matchers that can be used like this is possible{isCorsRequest: true, hasBody: true}
Response configuration
Responses are configured with the second, and sometimes third, arguments passed to .mock()
(or the first and second argument of .any()
or .catch()
). Where only one code sample is given below, it describes the second argument; otherwise the second and third are given. [Note - in the next major version these will all be available on the second argument]
Response
instancenew Response('hello world')
- status code
200
- text
hello world
- JSON
{prop: 'val'}
- streamed content
new Blob()
,{sendAsJson: false}
- headers
{body: 'hello world', status: 200, headers: {'Content-Type': 'text'}
- throw an error
{throws: new Error('fetch failed')}
- redirect
{redirectUrl: 'http://other.site, status: 302}
- function
(url, options, request) => `Content from ${url}`
Timing and repetition
- Respond a specified number of times
200
,{repeat: 3}
- Delay by a number of milliseconds
200
,{delay: 2000}
- Custom async behaviour using a Promise
myPromise.then(200)
Functions and Promises can be nested to any depth to implement complex race conditions
Inspecting calls
.calls()
retrieves a list of all calls matching certain conditions. It return an array of [url, options]
arrays, which also have a .request
property containng the original Request
instance.
Filtering
- all calls
.calls()
- matched calls
.calls(true)
or.calls('matched')
- unmatched calls
.calls(false)
or.calls('unmatched')
- calls to a named route
.calls('My route
) - calls to a url pattern used in a route
.calls('end:/path/bob
) - calls matching a matcher
.calls(anyValidMatcher)
- calls filtered by method
.calls(anyValidMatcher, 'POST')
Shortcut methods
These accept the same filters as above, but give a quicker route to answering common questions
- Do any calls match the filter?
.called()
- What was the last call
.lastCall()
- What was the last url
.lastUrl()
- What was the last options
.lastOptions()
Completion
- Check if all routes have been called as expected
.done()
- Check if all routes matching the filter have been called as expected
.done(filter)
(filter must be a route name or url pattern) - Wait for all fetches to respond
.flush()
(pass intrue
to wait for all bodies to be streamed). e.g.await fetchMock.flush(true)