MockRequests
Mocks network requests with desired static and dynamic responses automatically so you never have to change your source code to use mocks ever again.
Backed and Used by:
Contents
- Features
- Installation
- Usage
- Examples
- Separating mocks from source
- MockRequests API
- Final notes
- License
Features
This library was made for the purpose of allowing developers to be able to continue to write code as normal even when their APIs are down, haven't been developed by back-end teams yet, or don't have internet at all. It provides a quick, single point of entry that can be called once and will work throughout your entire app.
In particular, most other network-mocking libraries are not user friendly in that they either:
- Force users to re-write their source code to use mocks and then change it back later in order to use real network requests, or
- Involve complex setup using local servers and proxies, usually in ways that are app-specific and aren't easily transferable to other projects.
This library differs from the others in that it allows you to continue writing code as normal while still using mock network activity.
Specific benefits provided by this library that aren't offered in others:
- You never have to change your source code. This means no more replacing
fetch()withPromise.resolve(mockResponse), and no changing URLs fromwebsite.com/apitothird-party-mocks.com/api. - No painful configuration of complex node servers, proxies, or anything else to host mock data.
This also means no need to change URLs from
website.com/apitolocalhost/api. - Full support for use along with third-party libraries, including Axios and jest, so they function as normal while still giving you the mocks you want.
- Dynamically update mock responses based on request payloads, previous mock responses, and query parameters in order to mimic back-end alterations of data.
- Query parameter parsing so you can mock all URLs with the same pathname using the same dynamic response function.
- Customizable mock response delays to mimic natural network resolution times.
- Mix mocks with real API calls so you can use both functional and non-functional endpoints simultaneously.
- Simple configuration to split mock code from production code and/or activate mocks via terminal (e.g.
MOCK=true npm start). - No confusing interfaces or multi-step procedures to getting started. Simply configure it once and you're good to go. This becomes extremely useful if you want to switch app-wide mocks between different logins when developing.
- Greatly simplify API testing. Just define
fetchand/orXMLHttpRequestin a test setup file and configureMockRequestswith the responses you expect. It will handle all the heavy-lifting of mocking network responses for you so you don't have to repetitively use e.g.fetch = jest.fn(). - Compatible with all JavaScript environments, including back-end Node scripts, as long as either
fetchorXMLHttpRequestare defined and used in that environment (natively or by polyfill).
Installation
-
Using npm (see the npm package):
npm install --save-dev mock-requests -
Using git:
-
Via npm:
npm install --save-dev https://github.com/D-Pow/MockRequests.git -
With locally installed repo:
git clone https://github.com/D-Pow/MockRequests.gitpackage.json:
"mock-requests": "file:<pathToCloneLocation>/MockRequests
-
Usage
API docs can be viewed in the JSDoc along with a live demo.
This library wraps XMLHttpRequest and fetch such that any network request to a configured
URL will return the specified mock response instead of actually making the network requests.
Otherwise, if a URL hasn't been configured with a mock response, the standard async request is made.
MockRequests was designed to be used in such a way that wherever you configure it, the entire app
experiences the effects. This means you could configure it in one file and then all other files
that make network requests to the configured URLs will receive the mock responses instead,
even without importing MockRequests. This makes it very easy to work on the front-end even if
some APIs are down/haven't been developed yet, or if you have no internet access at all.
Examples
Note how in the below examples, the production-bound code doesn't change between mocking and using network calls.
Static responses
Standard configuration
To configure global app usage of MockRequests, simply call configure() with an object containing URL-responseObject
mappings.
// This is the only code you need to add to use this library
// Add in the MockConfig.js file described in the "Separating
// mocks from source code" section
import MockRequests from 'mock-requests';
import {
myApiUrl, // 'https://example.com/api/vx/someApi'
anotherUrl // '192.168.0.1'
} from '../src/services/Urls.js';
const myApiMockResponse = { someJson: 'responseObject' };
const anotherUrlMockResponse = '<html>some other type of response</html>';
MockRequests.configure({
[myApiUrl]: myApiMockResponse,
[anotherUrl]: anotherUrlMockResponse
});
// ...source code
// Using your async requests in source code
// This stays the same regardless of if you're using mocks or the actual endpoint
const jsonResponse = await fetch(myApiUrl).then(res => res.json());
const htmlResponse = await fetch(anotherUrl).then(res => res.text());
useResponseContentAsNormal(jsonResponse);
useResponseContentAsNormal(htmlResponse);Alternatively, you could configure URL-response content individually:
// same URLs and mock responses from above
import MockRequests from 'mock-requests';
MockRequests.setMockUrlResponse(myApiUrl, myApiMockResponse);
MockRequests.setMockUrlResponse(anotherUrl, anotherUrlMockResponse);Mixing mocks with actual API calls
In the event that some APIs are not functioning correctly but others are, you can configure
the broken APIs using MockRequests and then leave the other APIs as-is for normal responses:
import MockRequests from 'mock-requests';
import { nonfunctionalApi } from '../src/services/Urls.js';
MockRequests.setMockUrlResponse(nonfunctionalApi, /* mock response */);
// ...source code
// Will receive mock
const mockedResponse = await fetch(nonfunctionalApi).then(res => res.json());
// Will receive actual API response
const realApiResponse = await fetch(functionalApi).then(res => res.json());
useResponseContent(mockedResponse);
useResponseContent(realApiResponse);Dynamic responses
Dynamically modifying subsequent responses
This library also supports dynamically updating your mocked APIs' responses, so as to mimic actual
back-end systems. To utilize this feature, you'll need to call the dynamic counterparts of
configure/setMockUrlResponse (configureDynamicResponses/setDynamicMockUrlResponse) along with
a slightly modified config object that has response and dynamicResponseModFn fields:
import MockRequests from 'mock-requests';
import { myApiUrl } from '../src/services/Urls.js';
const initialMockResponse = {
data: ['a', 'b', 'c', 'd', 'e'],
value: 7
};
const dynamicConfig1 = {
[myApiUrl]: {
// The desired response is now nested inside the `response` property.
// This will be the initial default value of the `response` parameter
// in the function below, but won't be used after that.
response: initialMockResponse,
// The dynamicResponseModFn takes in the request and previous
// response as arguments to produce the new response.
// The new response **must** be returned from this function.
// Feel free to modify `response` as it will be deep-copied later.
dynamicResponseModFn: (request, response) => {
// Mix both request and response data to generate new response
response.data = response.data.concat(request.addLettersArray);
response.value += request.valueModification;
return response; // is actually now the new response
}
}
};
MockRequests.configureDynamicResponses(dynamicConfig1);
// ...source code
const payload = {
addLettersArray: ['f', 'g'],
valueModification: 5
};
const myDynamicallyModifiedResponse = await fetch(myApiUrl, {
body: JSON.stringify(payload)
}).then(res => res.json());
console.log(myDynamicallyModifiedResponse)
/* Will output:
{
data: ['a', 'b', 'c', 'd', 'e', 'f', 'g'],
value: 12
}
*/Mocking families of URLs using query parameters
Additionally, the dynamicResponseModFn will receive an object containing query parameters from the request URL,
which means you also have the option to generate dynamic responses based on those.
If you want to mock all URLs with the same pathname but different query parameters, simply add usePathnameForAllQueries: true
to your dynamic mock configuration.
Regardless of if you set usePathnameForAllQueries or not, dynamicResponseModFn will still receive the queryParamMap.
import {
searchApiPathname // 'https://example.com/search' e.g. search?q=weather
} from '../src/services/Urls.js';
MockRequests.setDynamicMockUrlResponse(searchApiPathname, {
// `response` field not needed because we don't need an initial `response` value
dynamicResponseModFn: (request, response, queryParamMap) => {
const searchQuery = decodeURIComponent(queryParamMap.q);
return `You searched for ${searchQuery}`;
},
usePathnameForAllQueries: true
});
// ...source code
const searchQuery = getSearchFromTextInput(); // let's assume this is `weather`
const searchUrl = `${searchApiPathname}?q=${encodeURIComponent(searchQuery)}`;
const response = await fetch(searchUrl).then(res => res.text());
console.log(response);
/* Will output:
'You searched for weather'
*/Delaying mock response resolutions
There is also a delay option you can use if you want to mimic network delays:
// or configureDynamicResponses({ [myApiUrl]: {...} })
MockRequests.setDynamicMockUrlResponse(myApiUrl, {
response: myMockResponse,
dynamicResponseModFn: (req, res, queries) => {/* ... */},
delay: 1500 // will make fetch take 1.5 seconds to resolve myApiUrl
});Sample usage with different logins
Finally, because the configure/setMockUrlResponse functions take in a simple URL-response mapping,
using different mocks at different times becomes incredibly user-friendly. For example,
if your data changes based on which user is logged in, then the MockRequests API is
particularly easy to work with. In this case, after defining each user's mock responses,
you could nest them in a single loginMocks object and simply choose which login to use:
const bobMocks = {
[homepageUrl]: bobHomepageMock,
[friendsUrl]: bobFriendsMock
};
const aliceMocks = {
[homepageUrl]: aliceHomepageMock,
[friendsUrl]: aliceFriendsMock
};
const loginMocks = {
bob: bobMocks,
alice: aliceMocks
};
// Today, I want to be Alice
MockRequests.configure(loginMocks.alice);
// no, wait, I'll be Bob instead
MockRequests.configure(loginMocks.bob);Other utility functions
For convenience, a mapStaticConfigToDynamic() function has been included to make converting the above
static version of loginMocks to the dynamic counterpart easier:
// Example 1
// Convert a static URL-response mock to dynamic and add delay to home page
const dynamicBobMocks = MockRequests.mapStaticConfigToDynamic(bobMocks);
dynamicBobMocks[homepageUrl].delay = 1500;
MockRequests.configureDynamicResponses(dynamicBobMocks);
// Example 2
// Convert all loginMocks entries to dynamic counterparts
const dynamicLoginMocks = Object.keys(loginMocks).reduce((dynamicConfigs, user) => {
dynamicConfigs[user] = MockRequests.mapStaticConfigToDynamic(loginMocks[user]);
return dynamicConfigs;
}, {});
MockRequests.configureDynamicResponses(dynamicLoginMocks.bob);
// Example 3
// Merge user-agnostic dynamic mocks with static loginMocks
const dynamicMocks = {
[searchApiPathname]: {
dynamicResponseModFn: (req, res, queries) => {
/* ... same as search query above */
}
}
};
const staticDynamicMerged = Object.keys(loginMocks).reduce((dynamicConfigs, user) => {
dynamicConfigs[user] = {
...MockRequests.mapStaticConfigToDynamic(loginMocks[user]),
...dynamicMocks
};
return dynamicConfigs;
}, {});
MockRequests.configureDynamicResponses(staticDynamicMerged.bob);Separating mocks from source code
Simple instructions
In the simplest, bare-bones example, you could just import MockRequests into one of your entry JavaScript files
(src/index.js, src/App.js, or similar) and configure your mocks there. As long as MockRequests was installed as a
devDependency and you don't commit this code, it will never enter production.
// src/index.js for React project
import React from 'react';
import ReactDOM from 'react-dom';
import App from '/components/App';
import MockRequests from 'mock-requests';
MockRequests.configure(/* ... */);
ReactDOM.render(<App />, document.getElementById('root'));However, for larger apps with many network calls or for sharing mocks with other team members, typing and removing
mocks can get quite cumbersome. To simplify this, you could simply move mock-related code to a separate mocks/
folder and only import them when needed. This way, you can commit the mock code to your repo but, just like
test code, it doesn't get deployed into production since it isn't in the src/ folder.
For example, if we have the setup:
MyApp
├─── src/
| ├─── (... source code)
├─── mocks/
| ├─── MockConfig.js
| ├─── StaticResponses.js
| ├─── DynamicResponseConfigs.js
| ├─── (... other mock files imported by MockConfig.js)
where MockConfig.js does all the mock-requests configuration, e.g.
// mocks/MockConfig.js
import MockRequests from 'mock-requests';
import { myStaticApiUrl, myDynamicApiUrl } from '../src/services/Urls.js';
// Imports from mocks/ directory
import { myStaticApiResponse } from './StaticResponses';
import { myDynamicApiConfig } from './DynamicResponseConfigs';
MockRequests.setMockUrlResponse(myStaticApiUrl, myStaticApiResponse);
MockRequests.setDynamicMockUrlResponse(myDynamicApiUrl, myDynamicApiConfig);then in your src/index.js file, just import the MockConfig.js file to activate all mocks, and don't commit that code change to keep all of it out of production:
import React from 'react';
import ReactDOM from 'react-dom';
import App from '/components/App';
import '../mocks/MockConfig';
ReactDOM.render(<App />, document.getElementById('root'));Activating via Terminal
If you prefer not having to change source code to activate/deactivate mocks, then the guide below will explain how to
activate mocks via terminal, e.g. MOCK=true npm start.
- Import the function from the built-in
mock-requests/bin/resolve-mocks.jsscript to handle activating/deactivating mocks for you. - Pass in the mocks/ directory, the MockConfig.js entry file, and a boolean that determines if mocks should be
active or not (e.g.
process.env.MOCK === 'true'). Returns aresolvedMocksobject withentryandincludearrays. - Spread the
resolvedMocks.entryarray in your webpack config'sentryfield. - (Optional) Spread the
resolvedMocks.includearray in your webpack config'sincludefield if you need the mock files to be transformed by your JS-loader to run your app on an older browser.
// webpack.config.js
// Returns an object containing arrays to spread in webpack's `include` and `entry` fields.
// resolveMocks(mockDirectory, mockEntryFile, activateMocksBoolean)
const resolveMocks = require('mock-requests/bin/resolve-mocks');
const resolvedMocks = resolveMocks('mocks', 'mocks/MockConfig.js', process.env.MOCK === 'true');
module.exports = {
entry: [ '@babel/polyfill', './src/index.js', ...resolvedMocks.entry ],
module: {
rules: [
{
test: /\.jsx?$/,
include: [ /src/, ...resolvedMocks.include ],
exclude: [ /node_modules/ ],
loader: 'babel-loader'
}
]
}
}and run using MOCK=true npm start.
Doing so will allow MockRequests to automatically decide whether the mock directory/file should be resolved.
It will return empty arrays if the activateMocksBoolean is false or arrays filled with resolved paths if true.
This way, your MockConfig.js file will be added as an entry point to your app only if desired and will be
loaded along with the rest of your code. Optionally, if you're using the latest JavaScript features in your
mock configuration files but want to run them on browsers that lack support, then you'll need those mock files transpiled
by your loader as well; in that case, add the respective spread to the include field as well.
In this way, you won't have to change source code between activating and deactivating mocks.
MockRequests API
In order to make mocking your network calls simpler, config functions have been added to allow for setting, getting, and deleting mock responses for your network calls. These are described at length in the JSDoc.
configure(staticUrlResponseConfigObject, overwritePreviousConfig = true)
configureDynamicResponses(dynamicUrlResponseConfigObject, overwritePreviousConfig = true)
setMockUrlResponse(url, staticMockResponseObject)
setDynamicMockUrlResponse(url, dynamicMockResponseObject)
getResponse(url)
deleteMockUrlResponse(urlNotMeantToBeMocked)
clearAllMocks()
mapStaticConfigToDynamic(staticConfig)
OriginalXHR()
originalFetch()
Note that OriginalXHR and originalFetch will use the original XMLHttpRequest and fetch respectively,
regardless of if you've set the mock URL responses in MockRequests.configure() or MockRequests.setMockUrlResponse(...).
It will also use XMLHttpRequest and fetch regardless of if the browser supports them or not (will be undefined in
cases where the browser doesn't support them).
Final notes
-
This mocks the usage of
XMLHttpRequestandfetchsuch that the response is always valid. This means that the instance attributes below are always set. If you want to change any of these, feel free to do so withinxhr.onreadystatechange/fetch().then(fn).For
XMLHttpRequest:xhr.readyState = 4; xhr.response = mockedResponse; xhr.responseText = stringVersionOf(mockedResponse); // either JSON.stringify(mockedResponse) or `${mockedResponse}` xhr.responseUrl = urlPassedInXhrOpenMethod; xhr.status = 200; xhr.statusText = 'OK'; xhr.timeout = 0;
For
fetch().then(response => ...):response.status = 200; response.statusText = ''; response.ok = true; response.headers = new Headers({ status: '200' }); response.redirected = false; response.type = 'basic';
-
This library also works with other members of the Fetch API, so you can alternatively use an instance of the
Requestclass in yourfetch()calls, e.g.fetch(new Request(url, options)). -
You may import either the
MockRequestsdefault export or any of its individual fields, e.g.
import MockRequests, { setMockUrlResponse } from 'mock-requests'; -
This works with any environment that uses either
fetchorXMLHttpRequest, regardless of if said environment is a browser, web/service worker, or a Node script. As long as they are defined (natively or by polyfill), any usage offetch/XMLHttpRequestrequest to a URL configured byMockRequestswill be mocked appropriately. For example,require('isomorphic-fetch'); const MockRequests = require('mock-requests'); // ... use fetch and MockRequests as normal
License
MIT

Formed in 2009, the Archive Team (not to be confused with the archive.org Archive-It Team) is a rogue archivist collective dedicated to saving copies of rapidly dying or deleted websites for the sake of history and digital heritage. The group is 100% composed of volunteers and interested parties, and has expanded into a large amount of related projects for saving online and digital history.
