XHR-Mock logo

XHR-Mock Edit

This is a utility for mocking XMLHttpRequest. It’s useful for testing, or prototyping while your backend is still being built.

It works in NodeJS and in the browser, and is compatible with:

It may also work with other libraries built on XMLHttpRequest.

Standard compliant (http://xhr.spec.whatwg.org/).

For more information, go to the XHR-Mock GitHub Page.

Installation Edit

With a Bundler

If you are using a bundler like Webpack or Browserify install xhr-mock using yarn or npm:

yarn add --dev xhr-mock

Now import xhr-mock and start using it in your scripts:

import mock from 'xhr-mock';
Without a Bundler

If you aren’t using a bundler like Webpack or Browserify then add this script to your HTML:

<script src="https://unpkg.com/xhr-mock/dist/xhr-mock.js"></script>

You can now start using the global, XHRMock, in your scripts!

Example Edit

./createUser.js demonstrates a use case of XMLHttpRequest.

Axios, jQuery, Superagent or another package can also be used instead of the native XMLHttpRequest object.

  1. A new XMLHttpRequest is created called xhr.
  2. the .onreadystatechange function is created for when the client receives the response from the server
  3. xhr.open initializes the request to the url.
  4. xhr.setRequestHeader sets the value of of the header, ‘Content-Type’ to JSON.
  5. xhr.send sends the request body to the server.

./createUser.test.js, shows unit tests using xhr-mock which replaces XMLHttpRequest with MockXMLHttpRequest.

  1. mock.post registers the url and POST method to the request handler.
  2. createUser method is called passing the parameter “John”.
  3. XHRMock processes the request to the url, and:
    • If the value of the request header is in JSON,
    • If the request data is equal to “John”,
  4. The response "id": "abc-123" is returned.
// createUser serves as the client.
export default function createUser(reqdata) {
  return new Promise((resolve, reject) => {
    // we create a new XMLHttpRequest object.
    const xhr = new XMLHttpRequest();
    // the onreadystatechange function is for receiving the response and checking the status.
    xhr.onreadystatechange = () => {
      if (xhr.readyState == XMLHttpRequest.DONE) {
        if (xhr.status === 201) {
          try {
            resolve(JSON.parse(xhr.responseText).data);
          } catch (error) {
            reject(error);
          }
        } else if (xhr.status) {
          try {
            reject(JSON.parse(xhr.responseText).error);
          } catch (error) {
            reject(error);
          }
        } else {
          eject(new Error('An error ocurred whilst sending the request.'));
        }
      }
    };
    // initializes the request to the url using the POST method, sets the request header value in JSON, and sends the request.
    xhr.open('post', '/api/user');
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.send(JSON.stringify({data: reqdata}));
  });
}
// Tests the code with the mock request and response.
import mock from 'xhr-mock';
import createUser from './createUser';

describe('createUser()', () => {
  // replaces the real XHR object with the mock XHR object before each test.
  beforeEach(() => mock.setup());

  // puts the real XHR object back and clears the mock after each test.
  afterEach(() => mock.teardown());

  it('should send the data as JSON', async () => {
    expect.assertions(2);
    // mock sets up the request function and registers the request from the client (createUser) to the request handler. If the request header is in JSON, and the request body is equal to "John," the response is returned: "id":"abc-123".
    mock.post('/api/user', (req, res) => {
      expect(req.header('Content-Type')).toEqual('application/json');
      expect(req.body()).toEqual('{"data":{"name":"John"}}');
      return res.status(201).body('{"data":{"id":"abc-123"}}');
    });
    // makes the requests as the client.
    await createUser({name: 'John'});
  });

  it('should resolve with some data when status=201', async () => {
    expect.assertions(1);

    mock.post('/api/user', {
      status: 201,
      reason: 'Created',
      body: '{"data":{"id":"abc-123"}}'
    });

    const user = await createUser({name: 'John'});

    expect(user).toEqual({id: 'abc-123'});
  });

  it('should reject with an error when status=400', async () => {
    expect.assertions(1);

    mock.post('/api/user', {
      status: 400,
      reason: 'Bad request',
      body: '{"error":"A user named \\"John\\" already exists."}'
    });

    try {
      const user = await createUser({name: 'John'});
    } catch (error) {
      expect(error).toMatch('A user named "John" already exists.');
    }
  });
});

XHRMock Edit

Methods used to mock the XMLHttpRequest object:

.setup()
  • description:
    • Replaces the global XMLHttpRequest object with the MockXMLHttpRequest.
.teardown()
  • description:
    • Restores the global XMLHttpRequest object to its original state.
.reset()
  • description:
    • Forgets all the request handlers.
.get( url | regex , mock )
  • description:
    • Registers mock as a factory function by passing it as a parameter to the .get function. When XHRMock receives GET request, it then uses the registered mock function to process the request. If the request is as expected, the mock returns a response. For greater detail, look at the source code.
  • parameters:
    • the url is passed into the function as either a string or a RegExp object.
    • mock is a factory function, MockFunction, passed into the .get function as a parameter.
.post( url | regex , mock )
  • description:
    • Registers mock as a factory function by passing it as a parameter to the .post function. When XHRMock receives POST request, it then uses the registered mock function to process the request. If the request is as expected, the mock returns a response. For greater detail, look at the source code.
  • parameters:
    • the url is passed into the function as either a string or a RegExp object.
    • mock is a factory function, MockFunction, passed into the .post function as a parameter.
.patch( url | regex , mock )
  • description:
    • Registers mock as a factory function by passing it as a parameter to the .patch function. When XHRMock receives PATCH request, it then uses the registered mock function to process the request. If the request is as expected, the mock returns a response. For greater detail, look at the source code.
  • parameters:
    • the url is passed into the function as either a string or a RegExp object.
    • mock is a factory function, MockFunction, passed into the .patch function as a parameter.
.delete( url | regex , mock )
  • description:
    • Registers mock as a factory function by passing it as a parameter to the .delete function. When XHRMock receives DELETE request, it then uses the registered mock function to process the request. If the request is as expected, the mock returns a response. For greater detail, look at the source code.
  • parameters:
    • the url is passed into the function as either a string or a RegExp object.
    • mock is a factory function, MockFunction, passed into the .delete function as a parameter.
.use( method , url | regex , mock )
  • description:
    • The .use function includes a method. .use registers mock as a factory function by passing it as a parameter to the .use function. When XHRMock receives USE request, it then uses the registered mock function to process the request. If the request is as expected, the mock returns a response. For greater detail, look at the source code.
  • parameters:
    • the method is passed as a string.
    • the url is passed into the function as either a string or a RegExp object.
    • mock is a factory function, MockFunction, passed into the .use function as a parameter.
.use( mock )
  • description:
    • Registers mock as a factory function to create mock responses for every request that passes through it. Url or method is not distinguished.
  • parameters:
    • mock is a factory function, MockFunction, passed into the .use function as a parameter.
.error( fn )
  • description:
    • Logs errors thrown by handlers.
  • parameters:
    • function

MockXMLHttpRequest Edit

MockXMLHttpRequest replaces XMLHttpRequest object.

For xhr-mock.setup() and xhr-mock.teardown() refer to XHRMock.

MockRequest

.method() : requestMethod
  • description:
    • Gets the request method.
  • return:
    • method: returns method as a string in one of the following: DELETE, GET, HEAD, OPTIONS, POST, or PUT.
.url() : MockURL
  • description:
    • Gets the request MockURL object.
  • return:
    • MockURL object.
.header( name , value )
  • description:
    • Sets the request header’s name and value.
  • parameters:
    • name : string
    • value: string
.header( name ) : value
  • description:
    • Gets a request header and returns the value as a string, or null if no header has been set.
  • parameters:
    • name: string
  • return:
    • value: string or null
.headers() : requestHeaders
  • description:
    • Gets the request headers and returns the value as an object.
  • return:
    • headers: object
.headers( requestHeaders )
  • description:
    • Sets the request headers.
  • parameters:
    • headers: object
.body() : requestBody
  • description:
    • Gets the request body.
  • return:
    • body: string
.body( requestBody )
  • description:
    • Sets the request body.
  • parameters:
    • body: string

MockResponse

.status() : value
  • description:
    • Gets the response status.
  • return:
    • value: number
.status( code )
  • description:
    • Sets the response status.
  • parameters:
    • code: number
.reason() : responseReason
  • description:
    • Gets the response reason.
  • return:
    • reason: string
.reason( phrase )
  • description:
    • Set the response reason.
  • parameters:
    • phrase: string
.header( name , value )
  • description:
    • Sets a response header.
  • parameters:
    • name: string
    • value: string
.header( name ) : value
  • description:
    • Gets a response header and returns the value as a string or null
  • parameters:
    • name: string
  • return:
    • value: string or null
.headers() : responseHeaders
  • description:
    • Get the response headers.
  • return:
    • headers: object
.headers( responseHeaders )
  • description:
    • Set the response headers.
  • parameters:
    • headers: object
.body() : responseBody
  • description:
    • Get the response body.
  • return:
    • body: string
.body( ResponseBody )
  • description:
    • Set the response body.
  • parameters:
    • body: string

MockFunction Edit

MockFunction

  • description:
    • function object used by XHRMock object for processing requests.
  • parameters:
    • req: MockRequest object.
    • res: MockResponse object.
  • return:
    • MockResponse object or undefined.

MockURL Edit

MockUrl

.protocol
  • property .protocol is a string and not required.
.username
  • property .username is a string and not required.
.password
  • property .password is a string and not required.
.host
  • property .host is a string and not required.
.path
  • property .path is a string and not required.
.port
  • property .port is a number and is not required.
.query
  • {[name: string]: string};
.hash
  • property .hash is a string and is not required.
.toString()
  • method .toString formats and returns the url as a string

Simulate progress Edit

Sets the Content-Length header and sends a body. xhr-mock will emit ProgressEvents.

import mock from 'xhr-mock';

mock.setup();

mock.post('/', {});

const xhr = new XMLHttpRequest();
xhr.upload.onprogress = event => console.log(event.loaded, event.total);
xhr.open('POST', '/');
xhr.setRequestHeader('Content-Length', '12');
xhr.send('Hello World!');
import mock from 'xhr-mock';

mock.setup();

mock.get('/', {
headers: {'Content-Length': '12'},
body: 'Hello World!'
});

const xhr = new XMLHttpRequest();
xhr.onprogress = event => console.log(event.loaded, event.total);
xhr.open('GET', '/');
xhr.send();

Simulate timeout Edit

Returns a Promise that never resolves or rejects.

A number of major libraries don’t use the timeout event and use setTimeout() instead. Therefore, in order to mock timeouts in major libraries, we have to wait for the specified amount of time.

import mock from 'xhr-mock';

mock.setup();

mock.get('/', () => new Promise(() => {}));

  const xhr = new XMLHttpRequest();
  xhr.timeout = 100;
  xhr.ontimeout = event => console.log('timeout');
  xhr.open('GET', '/');
  xhr.send();

Simulate error Edit

Returns a Promise that rejects. If you want to test a particular error you use one of the pre-defined error classes.

import mock from 'xhr-mock';

mock.setup();

mock.get('/', () => Promise.reject(new Error()));

const xhr = new XMLHttpRequest();
xhr.onerror = event => console.log('error');
xhr.open('GET', '/');
xhr.send();

Proxy requests Edit

If you want to mock some requests, but not all of them, you can proxy unhandled requests to a real server.

import mock, {proxy} from 'xhr-mock';

mock.setup();

// mock specific requests.
mock.post('/', {status: 204});

// proxy unhandled requests to the real servers.
mock.use(proxy);

// this request will receive a mocked response.
const xhr1 = new XMLHttpRequest();
xhr1.open('POST', '/');
xhr1.send();

// this request will receive the real response.
const xhr2 = new XMLHttpRequest();
xhr2.open('GET', 'https://jsonplaceholder.typicode.com/users/1');
xhr2.send();

Delay requests Edit

Requests can be delayed using our handy delay utility.

import mock, {delay} from 'xhr-mock';

mock.setup();

// delays the request for three seconds.
mock.post('/', delay({status: 201}, 3000));

One-off requests Edit

Requests can be made on one off occasions using our handy once utility.

import mock, {once} from 'xhr-mock';

mock.setup();

// the response will only be returned the first time a request is made.
mock.post('/', once({status: 201}));