A guide to Express request and response mocking/stubbing with Jest or sinon
Tags: javascript, node, testing, webdev
Published: 2019-02-18 08:24:38

---
title: A guide to Express request and response mocking/stubbing with Jest or sinon
published: true
tags: Javascript, Node, testing, #webdev
canonical_url: https://codewithhugo.com/express-request-response-mocking/
---

To test an Express handler, it’s useful to know how to successfully mock/stub the `request` and `response` objects. The following examples will be written both using Jest and sinon (running in AVA).

The rationale for this is the following. Jest is a very popular “all-in-one” testing framework. Sinon is one of the most popular “Standalone test spies, stubs and mocks for JavaScript” which “works with any unit testing framework”.

The approach detailed in this post will be about how to test handlers independently of the Express app instance by calling them directly with mocked request (`req`) and response (`res`) objects. This is only 1 approach to testing Express handlers and middleware. The alternative is to fire up the Express server (ideally in-memory using SuperTest). I go into more detail on how to achieve that in [“Testing an Express app with SuperTest, moxios and Jest”](https://dev.to/hugo__df/testing-an-express-app-with-supertest-moxios-and-jest-4f44).

One of the big conceptual leaps to testing Express applications with mocked request/response is understanding how to mock a chained API eg. `res.status(200).json({ foo: 'bar' })`.

This is achieved by returning the `res` instance from each of its methods:

```js
const mockResponse = {
  const res = {};
  // replace the following () => res
  // with your function stub/mock of choice
  // making sure they still return `res`
  res.status = () => res;
  res.json = () => res;
  return res;
};

```

See the repository with examples and the working application at [github.com/HugoDF/mock-express-request-response](https://github.com/HugoDF/mock-express-request-response).

Table of contents:

## Stubs and mocks: Jest.fn vs sinon

`jest.fn` and `sinon.stub` have the same role. They both return a mock/stub for a function. That just means a function that recalls information about its calls, eg. how many times and what arguments it was called with.

The Jest mock is tightly integrated with the rest of the framework. That means we can have assertions that look like the following:

```js
test('jest.fn recalls what it has been called with', () => {
  const mock = jest.fn();
  mock('a', 'b', 'c');
  expect(mock).toHaveBeenCalledTimes(1);
  expect(mock).toHaveBeenCalledWith('a', 'b', 'c');
});

```

Sinon is “just” a spies/stubs/mocks library, that means we need a separate test runner, the following example is equivalent to the previous Jest one but written using AVA:

```js
const test = require('ava');
const sinon = require('sinon');
test('sinon.stub recalls what it has been called with', t => {
  const mock = sinon.stub();
  mock('a', 'b', 'c');
  t.true(mock.called);
  t.true(mock.calledWith('a', 'b', 'c'));
});

```

## Mocking/stubbing a chained API: Express response

The Express user-land API is based around middleware. A middleware that takes a request (usually called `req`), a response (usually called `res` ) and a next (call next middleware) as parameters.

A “route handler” is a middleware that tends not to call `next`, it usually results in a response being sent.

An example of some route handlers are the following (in express-handlers.js).

In this example `req.session` is generated by `client-sessions`, a middleware by Mozilla that sets an encrypted cookie that gets set on the client (using a `Set-Cookie`). That’s beyond the scope of this post. For all intents and purposes, we could be accessing/writing to any other set of request/response properties.

```js
async function logout(req, res) {
  req.session.data = null;
  return res.status(200).json();
}
async function checkAuth(req, res) {
  if (!req.session.data) {
    return res.status(401).json();
  }
  const { username } = req.session.data;
  return res.status(200).json({ username });
}

module.exports = {
  logout,
  checkAuth
};

```

They are consumed by being “mounted” on an Express application (`app`) instance (in app.js):

```js
const express = require('express');
const app = express();

const { logout, checkAuth } = require('./express-handlers.js');

app.get('/session', checkAuth);
app.delete('/session', logout);

```

For the above code to work in an integrated manner, we need to also `app.use` the `client-sessions` package like so. Note that the `cookieName` is important since it’s the property under which the session gets set on the `req` object.

We also add the `express.json` middleware (Express 4.16+), which works like body-parser’s `.json()` option ie. it parses JSON bodies and stores the output in to `req.body`.

```js
const express = require('express');
const app = express();
const session = require('client-sessions');

app.use(express.json());
app.use(session({
  secret: process.env.SESSION_SECRET || 'my-super-secret',
  cookieName: 'session',
  duration: 60 * 60 * 1000 // 1 hour
}));

const { logout, checkAuth } = require('./express-handlers.js');

app.get('/session', checkAuth);
app.delete('/session', logout);

```

### Mocking/stubbing req (a simple Express request) with Jest or sinon

A mockRequest function needs to return a request-compatible object, which is a plain JavaScript object, it could look like the following, depending on what properties of `req` the code under test is using. Our code only accesses `req.session.data`, it means it’s expecting `req` to have a `session` property which is an object so that it can attempt to access the `req.session.data` property.

```js
const mockRequest = (sessionData) => {
  return {
    session: { data: sessionData },
  };
};

```

Since the above is just dealing with data, there’s no difference between mocking it in Jest or using sinon and the test runner of your choice (Mocha, AVA, tape, Jasmine…).

### Mocking/stubbing res (a simple Express response) with Jest

A mockResponse function would look like the following, our code under test only calls `status` and `json` functions. The issue we run into is that the calls are chained. This means that `status`, `json` and other `res` (Express response) methods return the `res` object itself.

That means that ideally our mock would behave in the same way:

```js
const mockResponse = () => {
  const res = {};
  res.status = jest.fn().mockReturnValue(res);
  res.json = jest.fn().mockReturnValue(res);
  return res;
};

```

We’re leveraging `jest.fn`’s `mockReturnValue` method to set the return value of both `status` and `json` to the mock response instance (`res`) they’re set on.

### Mocking/stubbing res (a simple Express response) with sinon

The sinon equivalent to the above (with a similar explanation) follows. With sinon, we have to explicitly `require` it since it’s a standalone library (ie. not injected by test frameworks).

Sinon stubs have a `returns` method which behaves like the `mockReturnValue` Jest mock method. It sets the return value of the stub.

The `status` and `json` methods on our mock response instance (`res`) return the response instance (`res`) itself.

```js
const sinon = require('sinon');

const mockResponse = () => {
  const res = {};
  res.status = sinon.stub().returns(res);
  res.json = sinon.stub().returns(res);
  return res;
};

```

### Testing a handler that reads from `req` and sends a `res` using status and json()

The `checkAuth` handler reads from `req` and sends a `res` using `status()` and `json()`.

It contains the following logic, if `session.data` is not set, the session is not set, and therefore the user is not authenticated, therefore it sends a `401 Unauthorized` status with an empty JSON body.Otherwise, it reflects the part of the session contents (just the `username`) in JSON response with a 200 status code.

Here’s the code under test (in express-handlers.js):

```js
async function checkAuth(req, res) {
  if (!req.session.data) {
    return res.status(401).json();
  }
  const { username } = req.session.data;
  return res.status(200).json({ username });
}

```

We need to test two paths: the one leading to a 401 and the other, leading to a 200.

See a snapshot of this code on GitHub [github.com/HugoDF/mock-express-request-response/releases/tag/check-auth-tests](https://github.com/HugoDF/mock-express-request-response/releases/tag/check-auth-tests) (click on the commit sha for the diff for that version change).

Using the `mockRequest` and `mockResponse` we’ve defined before, we’ll set a request that has no session data (for 401) and does have session data containing username (for 200). Then we’ll check that `req.status` is called with 401 and 200 respectively. In the 200 case we’ll also check that `res.json` is called with the right payload (`{ username }`).

In Jest (see express-handlers.jest-test.js):

```js
describe('checkAuth', () => {
  test('should 401 if session data is not set', async () => {
    const req = mockRequest();
    const res = mockResponse();
    await checkAuth(req, res);
    expect(res.status).toHaveBeenCalledWith(401);
  });
  test('should 200 with username from session if session data is set', async () => {
    const req = mockRequest({ username: 'hugo' });
    const res = mockResponse();
    await checkAuth(req, res);
    expect(res.status).toHaveBeenCalledWith(200);
    expect(res.json).toHaveBeenCalledWith({ username: 'hugo' });
  });
});

```

The same tests using sinon + AVA (in express-handlers.sinon-test.js):

```js
test('checkAuth > should 401 if session data is not set', async (t) => {
  const req = mockRequest();
  const res = mockResponse();
  await checkAuth(req, res);
  t.true(res.status.calledWith(401));
});

test('checkAuth > should 200 with username from session if data is set', async (t) => {
  const req = mockRequest({ username: 'hugo' });
  const res = mockResponse();
  await checkAuth(req, res);
  t.true(res.status.calledWith(200));
  t.true(res.json.calledWith({ username: 'hugo' }));
});

```

> See a snapshot of this code on GitHub [github.com/HugoDF/mock-express-request-response/releases/tag/check-auth-tests](https://github.com/HugoDF/mock-express-request-response/releases/tag/check-auth-tests) (click on the commit sha for the diff for that version change).

### Testing a handler that writes to `req` and sends a `res` using status and json()

The `logout` handler writes to req (it sets `req.session.data` to `null`) and sends a response using `res.status` and `res.json`. Here’s the code under test.

```js
async function logout(req, res) {
  req.session.data = null;
  return res.status(200).json();
}

```

It doesn’t have any branching logic, but we should test that `session.data` is reset and a response is sent in 2 separate tests. See a snapshot of this code on GitHub [github.com/HugoDF/mock-express-request-response/releases/tag/logout-tests](https://github.com/HugoDF/mock-express-request-response/releases/tag/logout-tests) (click on the commit sha for the diff for that version change).

In Jest, with the `mockRequest` and `mockResponse` functions (in express-handlers.jest-test.js):

```js
describe('logout', () => {
  test('should set session.data to null', async () => {
    const req = mockRequest({ username: 'hugo' });
    const res = mockResponse();
    await logout(req, res);
    expect(req.session.data).toBeNull();
  });
  test('should 200', async () => {
    const req = mockRequest({ username: 'hugo' });
    const res = mockResponse();
    await logout(req, res);
    expect(res.status).toHaveBeenCalledWith(200);
  });
});

```

In AVA + sinon using mockRequest and mockResponse functions (in express-handlers.sinon-test.js):

```js
test('logout > should set session.data to null', async (t) => {
  const req = mockRequest({ username: 'hugo' });
  const res = mockResponse();
  await logout(req, res);
  t.is(req.session.data, null);
});
test('logout > should 200', async (t) => {
  const req = mockRequest({ username: 'hugo' });
  const res = mockResponse();
  await logout(req, res);
  t.true(res.status.calledWith(200));
});

```

> See a snapshot of this code on GitHub [github.com/HugoDF/mock-express-request-response/releases/tag/logout-tests](https://github.com/HugoDF/mock-express-request-response/releases/tag/logout-tests) (click on the commit sha for the diff for that version change).

## A complex handler request/response mocking scenario: a request to login with a body

Our login handler does the heaviest lifting in the application. It’s in `express-handlers.js` and containts the following logic.

The login handler first validates that the contents of `req.body` and 400s if either of them are missing (this will be our first 2 tests).

The login handler then attempts to `getUser` for the given username, if there is no such user, it 401s (this will be our 3rd test).

Next the login handler compares the password from the request with the hashed/salted version coming from `getUser` output, if that comparison fails, it 401s (this will be our 4th test).

Finally, if the username/password are valid for a user, the login handler sets session.data to `{ username }` and sends a 201 response (this will be our 5th test).

The final test (that I haven’t implemented) that would make sense is to check that the handler sends a 500 if an error occurs during its execution (eg. `getUser` throws).

> See a snapshot of the code on GitHub [github.com/HugoDF/mock-express-request-response/releases/tag/login-tests](https://github.com/HugoDF/mock-express-request-response/releases/tag/login-tests) (click on the commit sha for the diff for that version change).

The login functions is as follows, for readability’s sake, I’ve omitted `getUser`. `getUser` is implemented as a hard-coded array lookup in any case whereas in your application it will be a database or API call of some sort (unless you’re using oAuth).

```js
const bcrypt = require('bcrypt');

async function login(req, res) {
  try {
    const { username, password } = req.body;
    if (!username || !password) {
      return res.status(400).json({ message: 'username and password are required' });
    }
    const user = getUser(username);
    if (!user) {
      return res.status(401).json({ message: 'No user with matching username' });
    }
    if (!(await bcrypt.compare(password, user.password))) {
      return res.status(401).json({ message: 'Wrong password' });
    }
    req.session.data = { username };
    return res.status(201).json();
  } catch (e) {
    console.error(`Error during login of "${req.body.username}": ${e.stack}`);
    res.status(500).json({ message: e.message });
  }
}

```

It’s consumed, by being “mounted” on the Express app in `app.js`:

```js
app.post('/session', login);

```

To be able to test the login function we need to extends the `mockRequest` function, it’s still returning a plain JavaScript object so there is not difference between our Jest and AVA + sinon version:

```js
const mockRequest = (sessionData, body) => ({
  session: { data: sessionData },
  body,
});

```

> See a snapshot of the code on GitHub [github.com/HugoDF/mock-express-request-response/releases/tag/login-tests](https://github.com/HugoDF/mock-express-request-response/releases/tag/login-tests) (click on the commit sha for the diff for that version change).

### Tests for login handler using in Jest

To test this Express handler thoroughly is a few more tests but fundamentally the same principles as in the `checkAuth` and `logout` handlers.

The tests look like the following (in express-handlers.jest-test.js):

```js
describe('login', () => {
  test('should 400 if username is missing from body', async () => {
    const req = mockRequest(
      {},
      { password: 'boss' }
    );
    const res = mockResponse();
    await login(req, res);
    expect(res.status).toHaveBeenCalledWith(400);
    expect(res.json).toHaveBeenCalledWith({
      message: 'username and password are required'
    });
  });
  test('should 400 if password is missing from body', async () => {
    const req = mockRequest(
      {},
      { username: 'hugo' }
    );
    const res = mockResponse();
    await login(req, res);
    expect(res.status).toHaveBeenCalledWith(400);
    expect(res.json).toHaveBeenCalledWith({
      message: 'username and password are required'
    });
  });
  test('should 401 with message if user with passed username does not exist', async () => {
    const req = mockRequest(
      {},
      {
        username: 'hugo-boss',
        password: 'boss'
      }
    );
    const res = mockResponse();
    await login(req, res);
    expect(res.status).toHaveBeenCalledWith(401);
    expect(res.json).toHaveBeenCalledWith({
      message: 'No user with matching username'
    });
  });
  test('should 401 with message if passed password does not match stored password', async () => {
    const req = mockRequest(
      {},
      {
        username: 'guest',
        password: 'not-good-password'
      }
    );
    const res = mockResponse();
    await login(req, res);
    expect(res.status).toHaveBeenCalledWith(401);
    expect(res.json).toHaveBeenCalledWith({
      message: 'Wrong password'
    });
  });
  test('should 201 and set session.data with username if user exists and right password provided', async () => {
    const req = mockRequest(
      {},
      {
        username: 'guest',
        password: 'guest-boss'
      }
    );
    const res = mockResponse();
    await login(req, res);
    expect(res.status).toHaveBeenCalledWith(201);
    expect(res.json).toHaveBeenCalled();
    expect(req.session.data).toEqual({
      username: 'guest',
    });
  });
});

```

> See a snapshot of the code on GitHub [github.com/HugoDF/mock-express-request-response/releases/tag/login-tests](https://github.com/HugoDF/mock-express-request-response/releases/tag/login-tests) (click on the commit sha for the diff for that version change).

### Tests for login handler using AVA + sinon

Again there’s nothing fundamentally new in these tests, they’re just denser and closer to what you would do in a real-world application, they are as follows (in express-handlers.sinon-test.js):

> See a snapshot of the code on GitHub [github.com/HugoDF/mock-express-request-response/releases/tag/login-tests](https://github.com/HugoDF/mock-express-request-response/releases/tag/login-tests) (click on the commit sha for the diff for that version change).

```js
test('login > should 400 if username is missing from body', async (t) => {
  const req = mockRequest(
    {},
    { password: 'boss' }
  );
  const res = mockResponse();
  await login(req, res);
  t.true(res.status.calledWith(400));
  t.true(res.json.calledWith({
    message: 'username and password are required'
  }));
});
test('should 400 if password is missing from body', async (t) => {
  const req = mockRequest(
    {},
    { username: 'hugo' }
  );
  const res = mockResponse();
  await login(req, res);
  t.true(res.status.calledWith(400));
  t.true(res.json.calledWith({
    message: 'username and password are required'
  }));
});
test('should 401 with message if user with passed username does not exist', async (t) => {
  const req = mockRequest(
    {},
    {
      username: 'hugo-boss',
      password: 'boss'
    }
  );
  const res = mockResponse();
  await login(req, res);
  t.true(res.status.calledWith(401));
  t.true(res.json.calledWith({
    message: 'No user with matching username'
  }));
});
test('should 401 with message if passed password does not match stored password', async (t) => {
  const req = mockRequest(
    {},
    {
      username: 'guest',
      password: 'not-good-password'
    }
  );
  const res = mockResponse();
  await login(req, res);
  t.true(res.status.calledWith(401));
  t.true(res.json.calledWith({
    message: 'Wrong password'
  }));
});
test('should 201 and set session.data with username if user exists and right password provided', async (t) => {
  const req = mockRequest(
    {},
    {
      username: 'guest',
      password: 'guest-boss'
    }
  );
  const res = mockResponse();
  await login(req, res);
  t.true(res.status.calledWith(201));
  t.true(res.json.called);
  t.deepEqual(
    req.session.data,
    { username: 'guest' }
  );
});

```

> See a snapshot of the code on GitHub [github.com/HugoDF/mock-express-request-response/releases/tag/login-tests](https://github.com/HugoDF/mock-express-request-response/releases/tag/login-tests) (click on the commit sha for the diff for that version change).

## Testing a middleware and mocking Express request.get headers

Another scenario in which you might want to mock/stub the Express request and response objects is when testing a middleware function.

Testing middleware is subtly different. A lot of middleware has conditions under which it does nothing (just calls `next()`). An Express middleware should always call `next()` (its 3rd parameter) or send a response.

Here’s an example middleware which allows authentication using an API key in an `Authorization` header of the format `Bearer {API_KEY}`.

Beyond the middleware vs handler differences, `headerAuth` is also using `req.get()`, which is used to get headers from the Express request.

I’ve omitted `apiKeyToUser` and `isApiKey`. `apiKeyToUser` is just a lookup from apiKeys to usernames. In a real-world application this would be a database lookup much like what would replace `getUser` in the `login` code.

```js
function headerAuth(req, res, next) {
  if (req.session.data) {
    return next()
  }
  const authenticationHeader = req.get('authorization')
  if(!authenticationHeader) {
    return next()
  }
  const apiKey = authenticationHeader
    .replace('Bearer', '')
    .trim();
  if (!isApiKey(apiKey)) {
    return next()
  }
  req.session.data = { username: apiKeyToUser[apiKey] };
  next();
}

```

> See a snapshot of the code on GitHub [github.com/HugoDF/mock-express-request-response/releases/tag/middleware-header-tests](https://github.com/HugoDF/mock-express-request-response/releases/tag/middleware-header-tests) (click on the commit sha for the diff for that version change).

### Updating mockRequest to support accessing headers

Here is a different version of mockRequest, it’s still a plain JavaScript object, and it mock `req.get` just enough to get the tests passing:

```js
const mockRequest = (authHeader, sessionData) => ({
  get(name) {
    if (name === 'authorization') return authHeader
    return null
  },
  session: { data: sessionData }
});

```

### Testing a middleware that accesses headers with Jest

Most of the tests check that nothing changes on the session while the middleware executes since it has a lot of short-circuit conditions.

Note how we pass a no-op function `() => {}` as the 3rd parameter (which is `next`).

```js
describe('headerAuthMiddleware', () => {
  test('should set req.session.data if API key is in authorization and is valid', async () => {
    const req = mockRequest('76b1e728-1c14-43f9-aa06-6de5cbc064c2');
    const res = mockResponse();
    await headerAuthMiddleware(req, res, () => {});
    expect(req.session.data).toEqual({ username: 'hugo' });
  });
  test('should not do anything if req.session.data is already set', async () => {
    const req = mockRequest('76b1e728-1c14-43f9-aa06-6de5cbc064c2', { username: 'guest' });
    const res = mockResponse();
    await headerAuthMiddleware(req, res, () => {});
    expect(req.session.data).toEqual({ username: 'guest' });
  });
  test('should not do anything if authorization header is not present', async () => {
    const req = mockRequest(undefined);
    const res = mockResponse();
    await headerAuthMiddleware(req, res, () => {});
    expect(req.session.data).toBeUndefined();
  });
  test('should not do anything if api key is invalid', async () => {
    const req = mockRequest('invalid-api-key');
    const res = mockResponse();
    await headerAuthMiddleware(req, res, () => {});
    expect(req.session.data).toBeUndefined();
  });
});

```

> See a snapshot of the code on GitHub [github.com/HugoDF/mock-express-request-response/releases/tag/middleware-header-tests](https://github.com/HugoDF/mock-express-request-response/releases/tag/middleware-header-tests) (click on the commit sha for the diff for that version change).

### Testing a middleware that accesses headers using AVA + sinon

Most of the tests check that nothing changes on the session while the middleware executes since it has a lot of short-circuit conditions.

Note how we pass a no-op function `() => {}` as the 3rd parameter (which is `next`).

```js
test('should set req.session.data if API key is in authorization and is valid', async (t) => {
  const req = mockRequest('76b1e728-1c14-43f9-aa06-6de5cbc064c2');
  const res = mockResponse();
  await headerAuthMiddleware(req, res, () => {});
  t.deepEqual(
    req.session.data,
    { username: 'hugo' }
  );
});
test('should not do anything if req.session.data is already set', async (t) => {
  const req = mockRequest('76b1e728-1c14-43f9-aa06-6de5cbc064c2', { username: 'guest' });
  const res = mockResponse();
  await headerAuthMiddleware(req, res, () => {});
  t.deepEqual(
    req.session.data,
    { username: 'guest' }
  );
});
test('should not do anything if authorization header is not present', async (t) => {
  const req = mockRequest(undefined);
  const res = mockResponse();
  await headerAuthMiddleware(req, res, () => {});
  t.is(req.session.data, undefined);
});
test('should not do anything if api key is invalid', async (t) => {
  const req = mockRequest('invalid-api-key');
  const res = mockResponse();
  await headerAuthMiddleware(req, res, () => {});
  t.is(req.session.data, undefined);
});

```

> See a snapshot of the code on GitHub [github.com/HugoDF/mock-express-request-response/releases/tag/middleware-header-tests](https://github.com/HugoDF/mock-express-request-response/releases/tag/middleware-header-tests) (click on the commit sha for the diff for that version change).

## Keys to testing Express handlers and middleware

There are a few keys to testing Express effectively in the manner outlined in this post.

First of all is understanding what the code does. It’s harder than it seems. Testing in JavaScript is a lot about understanding JavaScript, a bit about testing tools and a bit understanding the tools used in that application under test. In order to mock the tool’s return values with the right type of data.

All the tests in the post boil down to understanding what `req`, `res` and `next` are (an object, an object and a function). Which properties they have/can have, how those properties are used and whether they’re a function or an object.

This is only 1 approach to testing Express handlers and middleware. The alternative is to fire up the Express server (ideally in-memory using SuperTest). I go into more detail on how to achieve that in [“Testing an Express app with SuperTest, moxios and Jest”](https://dev.to/hugo__df/testing-an-express-app-with-supertest-moxios-and-jest-4f44)

[<svg xmlns="http://www.w3.org/2000/svg" style="height:12px;width:auto;position:relative;vertical-align:middle;top:-2px;fill:white" viewbox="0 0 32 32"><title>unsplash-logo</title>
<path d="M10 9V0h12v9H10zm12 5h10v18H0V14h10v9h12v-9z"></path></svg>Chris Barbalis](https://unsplash.com/@cbarbalis?utm_medium=referral&utm_campaign=photographer-credit&utm_content=creditBadge "Download free do whatever you want high-resolution photos from Chris Barbalis")