Unit testing validation with express-validator

express, node.js, unit-testing, validation

Solution

Here's a solution for the new express-validator api (v4):

tl;dr: You can use this function:

exports.testExpressValidatorMiddleware = async (req, res, middlewares) => {
  await Promise.all(middlewares.map(async (middleware) => {
    await middleware(req, res, () => undefined);
  }));
};

It can be called like this:

const { validationResult } = require('express-validator/check');

await testExpressValidatorMiddleware(req, res, expressValidatorMiddlewareArray);
const result = validationResult(req);
expect(result....

These solutions assume you have the async/await syntax available. You can use the node-mocks-http library to create the `req` and `res` objects.

Explanation:

Each element in an express-validator array is applied to the route as middleware. Say this is your array:

[
  check('addresses.*.street').exists(),
  check('addresses.*.postalCode').isPostalCode(),
]

Each check will be loaded as middleware.

In order to test middleware, we need to implement a function which acts similarly to how express implements middleware.

Express middleware always accepts three params, the request and response objects, and the next function it should call (`next` by convention). Why do we need `next`? For scenarios where we want our middleware to do something before and after the proceeding function, e.g.

const loggerMiddleware = (req, res, next) => {
  console.log('req body is ' + req.body);
  next();
  console.log('res status is ' + res.status);
};

But express-validator doesn't do this, it just calls `next()` once each of its validators is finished. For that reason, our implementation doesn't really need to bother with `next()`.

Instead, we can just run each of our middlewares in turn and pass an empty function as `next` to avoid a `TypeError`:

middlewares.map((middleware) => {
  middleware(req, res, () => undefined);
});

But this won't work, because express-validator middleware returns promises and we need to wait for them to resolve...

middlewares.map(async (middleware) => {
  await middleware(req, res, () => undefined);
});

And we don't want to move on until all promises in our iteration are resolved (Mozilla docs on `Promise.all` are here):

await Promise.all(middlewares.map(async (middleware) => {
  await middleware(req, res, () => undefined);
}));

And we should extract this as a reusable function:

exports.testExpressValidatorMiddleware = async (req, res, middlewares) => {
  await Promise.all(middlewares.map(async (middleware) => {
    await middleware(req, res, () => undefined);
  }));
};

And now we've arrived at my solution. If someone can improve on this implementation, I'm very happy to make edits.

Problem

How can I unit test my validations that are done using express-validator? I have tried creating a dummy request object, but I get the error: `TypeError: Object #<Object> has no method 'checkBody'`. I am able to manually test that the validation works in the application. Here is what I have tried: ``` describe('couponModel', function () { it('returns errors when necessary fields are empty', function(done){ var testBody = { merchant : '', startDate : '', endDate : '' }; var request = { body : testBody }; var errors = Model.validateCouponForm(request); errors.should.not.be.empty; done(); }); }); ``` My understanding is that the `checkBody` method is added to the request object when I have `app.use(expressValidator())` in my express application, but as I am only testing that the validation is working in this unit test I do not have an instance of the express application available, and the validation method that I am testing is not called directly from it anyway as it is only called through a post route, which I do not want to call for a unit test as it involves a database operation.

Original source