Introduction

fetch-mock allows mocking http requests made using fetch, or one of the many libraries imitating its api, such as node-fetch or fetch-ponyfill.

It supports most JavaScript environments, including nodejs, web workers and 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: 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:

Note that node-fetch is not included as a dependency of this package. This is to allow users a choice over which version to use; fetch-mock will use whichever version you have installed

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 nodejs 6 or lower use
require('fetch-mock/es5/server')

Global or non-global Parameter for installation()

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 nodejs process (a pattern sometiems used in isomorphic codebases)

By default fetch-mock assumes fetch is a global so once you’ve required fetch-mock, refer to the quickstart and api docs.

Non-global fetch library

In the following scenarios fetch will not be a global

  • Using node-fetch in nodejs 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, and can be passed into your choice of mocking library. The function returned by sandbox() supports the full fetch-mock api so once generated it can be worked with as if it were the original fetch-mock object, 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 Parameter for installation()

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 Parameter for installation()

Some fetch-mock internals require access to the Request, Response and Headers constructors provided by your chosen fetch implementation. These should be set on the fetchMock.config object

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

Configuration

On any fetchMock instance, config can be set by setting properties on fetchMock.config.

Options

sendAsJson
Boolean [default true]

Convert objects into JSON before delivering as stub reponses. Can be useful to set to false globally if e.g. dealing with a lot of array buffers. If true, will also add content-type: application/json header.

includeContentLength
Boolean [default true]

Automatically 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]

Determines behaviour if a new route has the same name (or inferred name) as an existing one

  • undefined: An error will be throw when routes clash
  • 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 the fallbackToNetwork option or catch())

Promise
Constructor

Reference to the Promise constructor of a custom Promise implementation

fetch
Function

Reference to a custom fetch implementation

Headers
Constructor

Reference to the Headers constructor of a custom fetch implementation

Request
Constructor

Reference to the Request constructor of a custom fetch implementation

Response
Constructor

Reference to the Response constructor of a custom fetch implementation

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)

Replaces fetch with a stub which records its calls and returns a Response instance.

Parameters

matcher
String | Regex | Function

Rule for matching calls to fetch

response
String | Object | Function | Promise | Response

Response to send matched calls

options
Object

More options configuring [mainly] matching behaviour

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

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 selecting which requests to mock. For matching based on headers, query strings or other fetch options see the options parameter documented below

Argument values

*
String

Matches any url

"*"
url
String

Matches an exact url

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

Matches a url beginning with a string

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

Matches a url ending with a string

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

Matches a url which has a given path

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

Matches a url using a glob pattern

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

Matches a url that matches an express style path

"express:/user/:user"
RegExp

Matches a url that matches a regular expression

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

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

(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

Response to send matched calls. Configures the http response returned by the mock. Can take any of the following values (or be a Promise for any of them, enabling full control when testing race conditions etc.). Unless otherwise stated, all responses have a 200 status

Argument values

Response

A Response instance - will be used unaltered

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

Creates 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

Creates a 200 response with the string as the response body

Server responded ok
Bad Response
config
Object

If an object only contains properties listed below it is used to configure a Response

body
String | Object

Set the response body. For behaviour for Objects, see the non-config Object section of the docs below

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 the response should claim to originate from (to imitate followed directs). Will also set redirected: true on the response

throws
Error

fetch will return a Promise rejected with the value of throws

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

All objects that do not meet the criteria above will be converted to JSON and set as the response body if the sendAsJson option is on. Otherwise, they will be set as the response body (useful for array buffers etc.)

Promise

A Promise that resolves to any of the options documented above

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

A function that is passed the arguments fetch is called with and that returns any of the responses listed above

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

options Parameter for mock()

Object

A configuration object with properties to define a route to mock

Options

name
String

A unique string naming the route. Used to subsequently retrieve references to the calls, grouped by name. Defaults to matcher.toString()

method
String

Http method to match

headers
Object | Headers

Key/value map of headers to match

query
Object

Key/value map of query strings to match, in any order

params
Object

When the express: keyword is used in a string matcher, a key/value map params can be passed here, to match the parameters extracted by express path matching

repeat
Integer

An integer, n, limiting the number of times the matcher can be used. If the route has already been called n times the route will be ignored and 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 defined above can be assigned to the options object

response

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

.once()

Shorthand for mock() which limits to being called one time only. (see repeat option above)

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

Shorthands for mock() restricted to a particular 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() restricted to a particular method and that will only respond once

.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 a reference to the fetch implementation you use in your code.

Inspection methods

Filtering basics

Most inspection methods take two arguments — filter and options — which allow groups of fetch calls to be extracted and inspected.

Parameters

filter

Allows filtering of calls to fetch based on various criteria

undefined

Retrieves all calls made to fetch, whether a specific route matched them or not

true

Retrieves all calls matched by fetch. fetchMock.MATCHED is an alias for true and may be used to make tests more readable

false

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

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()

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

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

If in doubt, add a name to your route, and pass in that name to retrieve exactly the calls you want.

Note that when matching calls handled by a route with a RegExp or function matcher, use 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

.calls(filter, options)

Returns an array of all calls to fetch matching the given filters. Each call is returned as a [url, options] array. If fetch was called using a Request instance, this 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

.lastUrl(filter, options)

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

.lastOptions(filter, options)

Returns the options for the call to fetch matching the given filter. 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

Runs over all calls matched by fetch

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()

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

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()

Returns a drop-in mock for fetch which can be passed to other mocking libraries. It implements the full fetch-mock api 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.

Pass in true to wait for all body parsing methods (res.json(), res.text(), etc.) to resolve too.

.restore(), .reset()

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

.resetHistory()

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

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 nodejs 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 nodejs 0.12 or lower use require('fetch-mock/es5/server')

Matching Request objects in node fails

In node, if using npm at a version less than 2 the Request constructor used by fetch-mock won’t necessarily be the same as the one used by node-fetch. To fix this either:

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