使用快速验证器进行单元测试验证

Car*_*sel 5 validation unit-testing node.js express

如何对使用express-validator完成的验证进行单元测试?

我尝试创建一个虚拟请求对象,但收到错误消息:TypeError: Object #<Object> has no method 'checkBody'。我能够手动测试验证在应用程序中是否有效。

这是我尝试过的:

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();
    });
});
Run Code Online (Sandbox Code Playgroud)

我的理解是,checkBody当我app.use(expressValidator())在快速应用程序中使用该方法时,该方法已添加到请求对象中,但是由于我仅测试该验证在此单元测试中有效,因此我没有可用的快速应用程序实例,并且该验证我正在测试的方法无论如何都不会直接从它调用,因为它只是通过发布路由来调用,我不想调用单元测试,因为它涉及数据库操作。

tce*_*act 7

这是新的 express-validator api (v4) 的解决方案:

tl; dr:您可以使用功能:

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

它可以被称为像这样

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

await testExpressValidatorMiddleware(req, res, expressValidatorMiddlewareArray);
const result = validationResult(req);
expect(result....
Run Code Online (Sandbox Code Playgroud)

这些解决方案假设您有可用的 async/await 语法。您可以使用node-mocks-http库来创建reqres对象。

解释:

express-validator 数组中的每个元素都作为中间件应用于路由。说这是你的数组:

[
  check('addresses.*.street').exists(),
  check('addresses.*.postalCode').isPostalCode(),
]
Run Code Online (Sandbox Code Playgroud)

每个检查都将作为中间件加载。

为了测试中间件,我们需要实现一个函数,它的作用类似于 express 实现中间件的方式。

Express 中间件总是接受三个参数,请求和响应对象,以及它应该调用的下一个函数(next按照约定)。我们为什么需要next?对于我们希望我们的中间件在处理函数之前之后做一些事情的场景,例如

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

但是 express-validator 不会这样做,它只是next()在每个验证器完成后调用。出于这个原因,我们的实现并不真正需要打扰next().

相反,我们可以依次运行每个中间件并传递一个空函数next以避免出现TypeError

middlewares.map((middleware) => {
  middleware(req, res, () => undefined);
});
Run Code Online (Sandbox Code Playgroud)

但这行不通,因为 express-validator 中间件返回承诺,我们需要等待它们解决......

middlewares.map(async (middleware) => {
  await middleware(req, res, () => undefined);
});
Run Code Online (Sandbox Code Playgroud)

我们不希望继续前进,直到我们迭代所有的承诺都解决了(Mozilla的文档Promise.all在这里):

await Promise.all(middlewares.map(async (middleware) => {
  await middleware(req, res, () => undefined);
}));
Run Code Online (Sandbox Code Playgroud)

我们应该将其提取为可重用的函数:

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

现在我们已经找到了我的解决方案。如果有人可以改进此实现,我很乐意进行编辑。


mar*_*nes 1

我遇到了同样的问题,我必须使用以下方法创建方法:

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;
}
Run Code Online (Sandbox Code Playgroud)

因此,在您的代码中您必须调用getValidInputRequest助手:

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();
  });
});
Run Code Online (Sandbox Code Playgroud)

现在,该request对象具有bodyexpress-validator所需的属性和所有方法。

如果你想测试验证器失败的情况,你应该使用这样的东西:

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;
}
Run Code Online (Sandbox Code Playgroud)

要更新请求,您应该执行以下操作:

request = getInvalidInputRequest(request, ['mandatory_param_1', 'mandatory_param_2']);
Run Code Online (Sandbox Code Playgroud)