Introduction

fetch-mock allows mocking http requests made using fetch or a library imitating its api, such as node-fetch or fetch-ponyfill.

It supports most JavaScript environments, including Node.js, web workers, service workers, and any browser that either supports fetch natively or that can have a fetch polyfill installed.

As well as shorthand methods for the simplest use cases, it offers a flexible API for customising all aspects of mocking behaviour.

fetchMock.mock('http://example.com', 200);
const res = await fetch('http://example.com');
assert(res.ok);
fetchMock.restore();

Quickstart

Setting up your mock

  • The commonest use case is fetchMock.mock(matcher, response), where matcher is an exact url or regex to match, and response is a status code, string or object literal.
  • You can also use fetchMock.once() to limit to a single call or fetchMock.get(), fetchMock.post() etc. to limit to a method.
  • All these methods are chainable so you can easily define several mocks in a single test.
fetchMock
  .get('http://good.com/', 200)
  .post('http://good.com/', 400)
  .get(/bad\.com/, 500)

Analysing calls to your mock

  • fetchMock.called(matcher) reports if any calls matched your mock (or leave matcher out if you just want to check fetch was called at all).
  • fetchMock.lastCall(), fetchMock.lastUrl() or fetchMock.lastOptions() give you access to the parameters last passed in to fetch.
  • fetchMock.done() will tell you if fetch was called the expected number of times.

Tearing down your mock

  • fetchMock.resetHistory() resets the call history.
  • fetchMock.reset() or fetchMock.restore() will also restore fetch() to its native implementation

Example

Example with Node.js: suppose we have a file make-request.js with a function that calls fetch:

module.exports = function makeRequest() {
  return fetch('http://httpbin.org/get').then(function(response) {
    return response.json();
  });
};

We can use fetch-mock to mock fetch. In mocked.js:

var fetchMock = require('fetch-mock');
var makeRequest = require('./make-request');

// Mock the fetch() global to always return the same value for GET
// requests to all URLs.
fetchMock.get('*', { hello: 'world' });

makeRequest().then(function(data) {
  console.log('got data', data);
});

// Unmock.
fetchMock.reset();

Result:

$ node mocked.js
'got data' { hello: 'world' }

Previous versions

Usage

Requirements

fetch-mock requires the following to run:

  • Node.js 8+ for full feature operation
  • Node.js 0.12+ with limitations
  • npm (normally comes with Node.js)
  • Either
    • node-fetch when testing in Node.js. To allow users a choice over which version to use, node-fetch is not included as a dependency of fetch-mock.
    • A browser that supports the fetch API either natively or via a polyfill/ponyfill

Installation

Install fetch-mock using

npm install --save-dev fetch-mock

In most environments use one of the following in your test files

const fetchMock = require('fetch-mock');

// Exposes constants that will make tests more readable
const { fetchMock, MATCHED, UNMATCHED } = require('fetch-mock');

Some exceptions include:

  • If your client-side code or tests do not use a loader that respects the browser field of package.json use require('fetch-mock/es5/client').
  • If you need to use fetch-mock without commonjs, you can include the precompiled node_modules/fetch-mock/es5/client-bundle.js in a script tag. This loads fetch-mock into the fetchMock global variable.
  • For server side tests running in Node.js 6 or lower use
    require('fetch-mock/es5/server')

Global or non-global

fetch can be used by your code globally or locally. It’s important to determine which one applies to your codebase as it will impact how you use fetch-mock

Global fetch

In the following scenarios fetch will be a global

  • When using native fetch (or a polyfill) in the browser
  • When node-fetch has been assigned to global in your Node.js process (a pattern sometimes used in isomorphic codebases)

By default fetch-mock assumes fetch is a global so no more setup is required once you’ve required fetch-mock.

Non-global fetch library

In the following scenarios fetch will not be a global

  • Using node-fetch in Node.js without assigning to global
  • Using fetch-ponyfill in the browser
  • Using libraries which use fetch-ponyfill internally
  • Some build setups result in a non-global fetch, though it may not always be obvious that this is the case

The sandbox() method returns a function that can be used as a drop-in replacement for fetch. Pass this into your mocking library of choice. The function returned by sandbox() has all the methods of fetch-mock exposed on it, e.g.

const fetchMock = require('fetch-mock');
const myMock = fetchMock.sandbox().mock('/home', 200);
// pass myMock in to your application code, instead of fetch, run it, then...
expect(myMock.called('/home')).to.be.true;

Polyfilling fetch

Many older browsers require polyfilling the fetch global. The following approaches can be used:

  • Add the following polyfill.io script to your test page
    <script src="https://polyfill.io/v2/polyfill?features=fetch"></script>

  • npm install whatwg-fetch and load ./node_modules/whatwg-fetch/fetch.js into the page, either in a script tag or by referencing in your test runner config.

Custom subclasses

fetch-mock uses Request, Response and Headers constructors internally, and obtains these from node-fetch in Node.js, or window in the browser. If you are using an alternative implementation of fetch you will need to configure fetch-mock to use its implementations of these constructors instead. These should be set on the fetchMock.config object, e.g.

const ponyfill = require('fetch-ponyfill')();
fetchMock.config = Object.assign(fetchMock.config, {
    Headers: ponyfill.Headers,
    Request: ponyfill.Request,
    Response: ponyfill.Response,
    fetch: ponyfill
})

Usage with Jest

Jest has rapidly become a very popular, full-featured testing library. Usage of fetch-mock with Jest is sufficiently different to previous libraries that it deserve some examples of its own:

If using global fetch, then no special treatment is required.

If assigning node-fetch to a variable in your source code, the following should provide a good manual mock for node-fetch, saved as ./__mocks__/node-fetch.js in your project.

const nodeFetch = jest.requireActual('node-fetch');
const fetchMock = require('fetch-mock').sandbox();
Object.assign(fetchMock.config, nodeFetch, {
  fetch: nodeFetch
});
module.exports = fetchMock;

When using a webpack based compilation step, something like the following may be necessary instead

const fetchMock = require('fetch-mock').sandbox();
const nodeFetch = require('node-fetch');
nodeFetch.default = fetchMock;

Configuration

On any fetch-mock instance, set configuration options directly on the fetchMock.config object. e.g.

const fetchMock = require('fetch-mock');
fetchMock.config.sendAsJson = false;

Options

sendAsJson
Boolean [default true]

Always convert objects passed to .mock() to JSON strings before building reponses. Can be useful to set to false globally if e.g. dealing with a lot of ArrayBuffers. When true the Content-Type: application/json header will also be set on each response.

includeContentLength
Boolean [default true]

Sets a Content-Length header on each response.

fallbackToNetwork
Boolean | String [default false]
  • true: Unhandled calls fall through to the network
  • false: Unhandled calls throw an error
  • 'always': All calls fall through to the network, effectively disabling fetch-mock.
overwriteRoutes
Boolean [default undefined]

Configures behaviour when attempting to add a new route with the same name (or inferred name) as an existing one

  • undefined: An error will be thrown
  • true: Overwrites the existing route
  • false: Appends the new route to the list of routes
warnOnFallback
Boolean [default true]

Print a warning if any call is caught by a fallback handler (set using catch(), spy() or the fallbackToNetwork option)

Promise
Constructor

A custom Promise constructor, if your application uses one

fetch
Function

A custom fetch implementation, if your application uses one

Headers
Constructor

The Headers constructor of a custom fetch implementation, if your application uses one

Request
Constructor

The Request constructor of a custom fetch implementation, if your application uses one

Response
Constructor

The Response constructor of a custom fetch implementation, if your application uses one

Options marked with a can also be overridden for individual calls to .mock(matcher, response, options) by setting as properties on the options parameter

Mocking API

.mock(matcher, response, options)

Initialises or extends a stub implementation of fetch, applying a route that matches matcher, delivers a Response configured using response, and that respects the additional options. The stub will record its calls so they can be inspected later. If .mock() is called on the top level fetch-mock instance, this stub function will also replace fetch globally.

Parameters

matcher
String | Regex | Function

Rule for which calls to fetch should match this route

response
String | Object | Function | Promise | Response

Response to send when a call is matched

options
Object

More options to configure matching and responding behaviour

Alternatively a single parameter, options, an Object with matcher, response and other options defined, can be passed

In the documentation “route” is often used to refer to the combination of matching and responding behaviour set up using a single call to mock()

fetchMock
  .mock('http://it.at.here/route', 200)
  .mock('begin:http://it', 200)
  .mock('end:here/route', 200)
  .mock('path:/route', 200)
  .mock('*', 200)
fetchMock
  .mock(/.*\.here.*/, 200)
  .mock((url, opts) => opts.method === 'patch', 200)
  .mock('express:/:type/:id', 200, {
    params: {
      type: 'shoe'
    }
  })
fetchMock
  .mock('*', 'ok')
  .mock('*', 404)
  .mock('*', {results: []})
  .mock('*', {throw: new Error('Bad kitty')))
  .mock('*', new Promise(res => setTimeout(res, 1000, 404)))
fetchMock
  .mock('begin:http://it.at.here/api', 200, {
    headers: {
      authorization: 'Basic dummy-token'
    }
  })
  .mock('begin:http://it.at.here/api', 403)

callApi('/endpoint', 'dummy-token')
  .then(res => {
    expect(res.status).to.equal(200)
  })

matcher  parameter for mock()

String | RegExp | Function

Condition for deciding which requests to mock. For matching headers, query strings or other fetch options see the options parameter

Argument values

*
String

Match any url

"*"
url
String

Match an exact url

"http://www.site.com/page.html"
begin:...
String

Match a url beginning with a string

"begin:http://www.site.com"
end:...
String

Match a url ending with a string

"end:.jpg"
path:...
String

Match a url which has a given path

"path:/posts/2018/7/3"
glob:...
String

Match a url using a glob pattern

"glob:http://*.*"
express:...
String

Match a url that satisfies an express style path

"express:/user/:user"
RegExp

Match a url that satisfies a regular expression

/(article|post)/\d+/
Function

Match if a function returns something truthy. The function will be passed the url and options fetch was called with. If fetch was called with a Request instance, it will be passed url and options inferred from the Request instance, with the original Request will be passed as a third argument.

This can also be set as a functionMatcher in the options parameter, and in this way powerful arbitrary matching criteria can be combined with the ease of the declarative matching rules above.

(url, {headers}) => !!headers.Authorization
(_, _, request) => !!request.headers.get('Authorization')

Note that if using end: or an exact url matcher, fetch-mock (for good reason) is unable to distinguish whether URLs without a path end in a trailing slash or not i.e. http://thing is treated the same as http://thing/

response  parameter for mock()

String | Object | Function | Promise | Response

Configures the http response returned by the mock. Accepts any of the following values or a Promise for any of them (useful when testing race conditions etc.). Unless otherwise stated, all responses have a 200 status

Argument values

Response

A Response instance to return unaltered.

Note that it must use the same constructor as that used in the fetch implementation your application uses. See how to configure this

new Response('ok', {status: 200})
status code
Integer

Return a Response with the given status code. The response’s statusText will also be set to the default value corresponding to the status

200, 404, 503
String

Return a 200 Response with the string as the response body

Server responded ok
Bad Response
config
Object

If an object only contains properties from among those listed below it is used to configure a Response to return

body
String | Object

Set the Response body. See the non-config Object section of the docs below for behaviour when passed an Object

Server responded ok
{ token: 'abcdef' }
status
Integer

Set the Response status

200, 404, 503
headers
Object

Set the Response headers

{'Content-Type': 'text/html'}
redirectUrl
String

The url from which the Response should claim to originate from (to imitate followed directs). Will also set redirected: true on the response

throws
Error

Forrce fetch to return a Promise rejected with the value of throws

new TypeError('Failed to fetch')
Object | ArrayBuffer | ...

If the sendAsJson option is set to true, any object that does not meet the criteria above will be converted to a JSON string and set as the response body. Otherwise, the object will be set as the response body (useful for ArrayBuffers etc.)

Promise

A Promise that resolves to any of the options documented above

new Promise(res => setTimeout(() => res(200), 50))
Function

A function that returns any of the options documented above. The function will be passed the url and options fetch was called with. If fetch was called with a Request instance, it will be passed url and options inferred from the Request instance, with the original Request will be passed as a third argument.

(url, opts) => opts.headers.Authorization ? 200 : 403
(_, _, request) => request.headers.get('Authorization') ?  200 : 403

options  parameter for mock()

Object

An object containing further options for configuring mocking behaviour

Options

name
String

A unique string naming the route. Used to subsequently retrieve references to the calls handled by it

method
String

Match only requests using this http method. Not case-sensitive

get, POST
headers
Object | Headers

Match only requests that have these headers set

{"Accepts": "text/html"}
query
Object

Match only requests that have these query parameters set (in any order)

{"q": "cute+kittenz", "format": "gif"}
params
Object

When the express: keyword is used in a string matcher, match only requests with these express parameters

{"section": "feed", "user": "geoff"}
functionMatcher
Object

For matching requests against arbitrary criteria. See the documentation on Function matchers

repeat
Integer

Limits the number of times the route can be used. If the route has already been called repeat times, the call to fetch() will fall through to be handled by any other routes defined (which may eventually result in an error if nothing matches it)

overwriteRoutes
Boolean

See global configuration

sendAsJson
Boolean [default true]

See global configuration

includeContentLength
Boolean [default true]

See global configuration

matcher

When using the single argument variant of .mock(), any valid matcher as documented above can be assigned to the options object

response

When using the single argument variant of .mock(), any valid response as documented above can be assigned to the options object

.once()

Shorthand for mock() which creates a route that can only mock a single request. (see repeat option above)

.get(), .post(), .put(), .delete(), .head(), .patch()

Shorthands for mock() that create routes that only respond to requests using a particular http method.

If you use some other method a lot you can easily define your own shorthands e.g.

fetchMock.purge = function (matcher, response, options) {
    return this.mock(
      matcher,
      response,
      Object.assign({}, options, {method: 'PURGE'})
    );
}

.getOnce(), .postOnce(), .putOnce(), .deleteOnce(), .headOnce(), .patchOnce()

Shorthands for mock() that create routes that only respond to requests using a particular http method, and that can only mock a single request.

.catch(response)

Specifies how to respond to calls to fetch that don’t match any mocks.

It accepts any valid fetch-mock response, and can also take an arbitrary function to completely customise behaviour. If no argument is passed, then every unmatched call will receive a 200 response

.spy()

Records call history while passing each call on to fetch to be handled by the network

To use .spy() on a sandboxed fetchMock, fetchMock.config.fetch must be set to the same fetch implementation used in your application. See how to configure this

Inspection methods

Inspection fundamentals

fetch-mock’s inspection methods allow information about how fetch was called to be retrieved after your application code has run. Most inspection methods take two arguments — filter and options — which allow individual, or groups of, fetch calls to be extracted and examined.

Parameters

filter

Filter calls to fetch using one of the following criteria:

undefined

Retrieve all calls made to fetch

true

Retrieve all calls to fetch matched by some route defined by fetch-mock. fetchMock.MATCHED is an alias for true and may be used to make tests more readable

const {MATCHED, fetchMock} = require('fetch-mock');
...
fetchMock.calls(MATCHED)
false

Retrieves all calls to fetch not matched by a route (i.e. those handled by catch(), spy() or the fallbackToNetwork option). fetchMock.UNMATCHED is an alias for false and may be used to make tests more readable

const {UNMATCHED, fetchMock} = require('fetch-mock');
...
fetchMock.calls(UNMATCHED)
routeIdentifier
String|RegExp|function

All routes have an identifier:

  • If it’s a named route, the identifier is the route’s name
  • If the route is unnamed, the identifier is the value of the matcher argument that was passed in to .mock()

All calls that were handled by the route with the given identifier will be retrieved

matcher
String|RegExp|function

Any matcher compatible with the mocking api can be passed in to filter the calls arbitrarily. The matcher will be executed using exactly the same rules as the mocking api

options
Object | String

Either an object compatible with the mocking api or a string specifying a http method to filter by. This will be used to filter the list of calls further

The filtering API is powerful, but potentially confusing. If in doubt, add a name to your route, and pass that name in to retrieve exactly the calls you want.

A note on Regular Expression and Function matchers

To retrieve calls handled by a route with a RegExp or function matcher, use a reference to the exact RegExp|function you used in your mock, e.g.

const matcherRX = /user\/biff/
fm.mock(matcherRX, 200)
...
fm.called(matcherRX)

not

fm.mock(/user\/biff/, 200)
...
fm.called(/user\/biff/)

The second example will retrieve the expected calls in simple test scenarios because if no routes match using the identifier the RegExp will be executed as a RegExp matcher. But in more complex scenarios where e.g. there are several routes handling similar paths, it might retrieve calls that were actually handled by different, similar route e.g.

const matcherRX = /user\/biff/
fm
  .mock('end:user/biff')
  .mock(matcherRX, 200)
...
// this will retrieve calls handled by either route
fm.called(/user\/biff/)
// this will retrieve only calls handeld by the second route
fm.called(/user\/biff/)

.called(filter, options)

Returns a Boolean indicating whether any calls to fetch matched the given filter and options

.calls(filter, options)

Returns an array of all calls to fetch matching the given filter and options. Each call is returned as a [url, options] array. If fetch was called using a Request instance, the url and options will be inferred from it, and the original Request will be available as a request property on this array.

.lastCall(filter, options)

Returns the arguments for the last call to fetch matching the given filter and options. The call is returned as a [url, options] array. If fetch was called using a Request instance, the url and options will be inferred from it, and the original Request will be available as a request property on this array.

.lastUrl(filter, options)

Returns the url for the last call to fetch matching the given filter and options. If fetch was last called using a Request instance, the url will be inferred from this

.lastOptions(filter, options)

Returns the options for the call to fetch matching the given filter and options. If fetch was last called using a Request instance, a set of options inferred from the Request will be returned

.done(filter)

Returns a Boolean indicating whether fetch was called the expected number of times (or has been called at least once if repeat is undefined for the route)

Parameters

matcherOrName

Rule for matching calls to fetch.

undefined | true

Returns true if all routes have been called the expected number of times

routeIdentifier
String|RegExp|function

All routes have an identifier:

  • If it’s a named route, the identifier is the route’s name
  • If the route is unnamed, the identifier is the matcher passed in to .mock()

Returns true if the routes specified by the identifier has been called the expected number of times

If several routes have the same matcher/url, but use mocking options, the recommended way to handle this is to name each route and filter using those names

Lifecycle methods

.sandbox()

eturns a function that can be used as a drop-in replacement for fetch. Pass this into your mocking library of choice. The function returned by sandbox() has all the methods of fetch-mock exposed on it and maintains its own state independent of other instances, so tests can be run in parallel.

fetchMock
  .sandbox()
  .mock('http://domain.com', 200)

.flush(waitForBody)

Returns a Promise that resolves once all fetches handled by fetch-mock have resolved

Useful for testing code that uses fetch but doesn’t return a promise.

If waitForBody is true, the promise will wait for all body parsing methods (res.json(), res.text(), etc.) to resolve too.

.restore(), .reset()

Resets fetch() to its unstubbed state and clears all data recorded for its calls. restore() is an alias for reset()

Both methods are bound to fetchMock, and can be used directly as callbacks e.g. afterEach(fetchMock.reset) will work just fine. There is no need for afterEach(function () {fetchMock.reset()})

.resetHistory()

Clears all data recorded for fetch()’s calls. It will not restore fetch to its default implementation

resetHistory() is bound to fetchMock, and can be used directly as a callback e.g. afterEach(fetchMock.resetHistory) will work just fine. There is no need for afterEach(function () {fetchMock.resetHistory()})

Troubleshooting

General

fetch is assigned to a local variable, not a global

First of all, consider whether you could just use fetch as a global. Here are 3 reasons why this is a good idea:

  • The fetch standard defines it as a global (and in some cases it won’t work unless bound to window), so to write isomorphic code it’s probably best to stick to this pattern
  • isomorphic-fetch takes care of installing it as a global in Node.js or the browser, so there’s no effort on your part to do so.
  • fetch-mock is primarily designed to work with fetch as a global and your experience of using it will be far more straightforward if you follow this pattern

Still not convinced?

In that case fetchMock.sandbox() can be used to generate a function which you can pass in to a mock loading library such as mockery instead of fetch

fetch doesn’t seem to be getting mocked?

  • If using a mock loading library such as mockery, are you requiring the module you’re testing after registering fetch-mock with the mock loader? You probably should be (Example incorrect usage). If you’re using ES6 import it may not be possible to do this without reverting to using require() sometimes.
  • If using isomorphic-fetch in your source, are you assigning it to a fetch variable? You shouldn’t be i.e.
    • import 'isomorphic-fetch', not import fetch from 'isomorphic-fetch'
    • require('isomorphic-fetch'), not const fetch = require('isomorphic-fetch')

Environment doesn’t support requiring fetch-mock?

  • If your client-side code or tests do not use a loader that respects the browser field of package.json use require('fetch-mock/es5/client').
  • If you need to use fetch-mock without commonjs, you can include the precompiled node_modules/fetch-mock/es5/client-browserified.js in a script tag. This loads fetch-mock into the fetchMock global variable.
  • For server side tests running in Node.js 0.12 or lower use require('fetch-mock/es5/server')

Matching Request objects in node fails

In node, if your Request object is not an instance of the Request constructor used by fetch-mock, you need to set a reference to your custom request class. This needs to be done if you are mocking the Request object for a test or you are running npm with a version below 3.

  • use fetchMock.config.Request = myRequest, where myRequest is a reference to the Request constructor used in your application code.