Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing validation with express-validator

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.

like image 700
Carasel Avatar asked Feb 27 '15 15:02

Carasel


People also ask

What is the use of express validator?

According to the official website, Express Validator is a set of Express. js middleware that wraps validator. js , a library that provides validator and sanitizer functions. Simply said, Express Validator is an Express middleware library that you can incorporate in your apps for server-side data validation.

What is ValidationResult?

ValidationResult(String, IEnumerable<String>) Initializes a new instance of the ValidationResult class by using an error message and a list of members that have validation errors. ValidationResult(ValidationResult) Initializes a new instance of the ValidationResult class by using a ValidationResult object.

Is Express Validator a middleware?

Introduction to express-validatorExpress-validator is an express middleware that provides us with validation and sanitization functions. Actually, Express validator is built on top of validator. js.

What is express validator NPM?

express-validator is a set of express. js middlewares that wraps validator. js validator and sanitizer functions.


2 Answers

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.

like image 89
achalk Avatar answered Oct 08 '22 04:10

achalk


I faced the same issue and I had to create the methods using this:

var validRequest = {
  // Default validations used
  checkBody: function () { return this; },
  checkQuery: function () { return this; },
  notEmpty: function () { return this; },

  // Custom validations used
  isArray: function () { return this; },
  gte: function () { return this; },

  // Validation errors
  validationErrors: function () { return false; }
};

function getValidInputRequest(request) {
    Object.assign(request, validRequest);
    return request;
}

So, in your code you have to call the getValidInputRequest helper:

describe('couponModel', function () {
  it('returns errors when necessary fields are empty', function(done){
      var testBody = {
          merchant : '',
          startDate : '',
          endDate : ''
      };
      var request = {
          body : testBody
      };

      request = getValidInputRequest(request); // <-- Update the request

      var errors = Model.validateCouponForm(request);
      errors.should.not.be.empty;
      done();
  });
});

Now, the request object has the body property and all the methods needed by express-validator.

If you want to test the cases that the validator fails, you should use something like this:

function getInvalidInputRequest(request, errorParams) {
    // Get de default valid request
    Object.assign(request, validRequest);

    // Override the validationErrors function with desired errors
    request.validationErrors = function () {
        var errors = [];
        errorParams.forEach(function(error){
            errors.push({msg: 'the parameter "'+ error +'" is mandatory'})
        });
        return errors;
    };
    return request;
}

And to update the request you should do:

request = getInvalidInputRequest(request, ['mandatory_param_1', 'mandatory_param_2']);
like image 21
marc_aragones Avatar answered Oct 08 '22 05:10

marc_aragones