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.