使用自定义错误处理程序时,是否可以使用 Mocha 测试 ExpressJS 中的错误处理?

Is there a way to test error handling in ExpressJS with Mocha when using a custom error handler?

测试

    it('should fail trying to GET bookmarks with false user id',async () => {
      try {
        const response = await request(app)
          .get(baseApiUrlUnderTest + 'false_user_id/bookmarks')
          .set('Authorization', bearerToken);
      } catch (e) {
        console.log(e); //it doesn't reach this point
        expect(e.httpStatus).to.equal(HttpStatus.UNAUTHORIZED);
      }
    });

被测方法相关部分:

/* GET bookmark of user */
personalBookmarksRouter.get('/', keycloak.protect(), wrapAsync(async (request, response) => {

  userIdTokenValidator.validateUserIdInToken(request);
 ...
}));

其中 wrapAsync 确保将错误传递给自定义错误处理程序:

let wrapAsync = function (fn) {
  return function(req, res, next) {
    // Make sure to `.catch()` any errors and pass them along to the `next()`
    // middleware in the chain, in this case the error handler.
    fn(req, res, next).catch(next);
  };
}

导致被测方法抛出异常的validateUserIdInToken方法:

const AppError = require('../models/error');
const HttpStatus = require('http-status-codes');

let validateUserIdInToken = function (request) {
  const userId = request.kauth.grant.access_token.content.sub;
  if ( userId !== request.params.userId ) {
    throw new AppError(HttpStatus.UNAUTHORIZED, 'Unauthorized', ['the userId does not match the subject in the access token']);
  }
}

module.exports.validateUserIdInToken = validateUserIdInToken;

以及根中间件中的自定义错误处理程序:

app.use(function(err, req, res, next) {
  if (res.headersSent) {
    return next(err)
  }
  if(err instanceof AppError) { //execution lands here as expected and the test stops...
    res.status(err.httpStatus);
    return res.send(err);
  } else {
    res.status(err.status || HttpStatus.INTERNAL_SERVER_ERROR);
    res.send({
      message: err.message,
      error: {}
    });
  }

});

我认为您可能没有正确处理这个问题。无效的身份验证不应在应用程序中引发错误 - 这实际上不是错误,而是验证问题。

如果身份验证失败,只需将相关的 http 错误代码 - 401 发送回客户端即可。

res.send(HttpStatus.UNAUTHORIZED, 'a message if you want'); // 401

在您的路由处理程序中:

personalBookmarksRouter.get('/', keycloak.protect(), wrapAsync(async (request, response) => {
  const userId = request.kauth.grant.access_token.content.sub;
  if ( userId !== request.params.userId ) {
     return response.send(HttpStatus.UNAUTHORIZED);
  }

 ...
}));

在您的测试中,检查状态 401:

chai.request(server)
    .get('/false_user_id/bookmarks')
    .end((err, result) => {
        if (err) {
            return callback(err);
        }

        result.should.have.status(401);
    });

感谢 @laggingreflex 的评论,我错过了调试,即响应实际上返回了预期的状态和错误消息

调整后的测试用例现在如下所示:

    it('should fail trying to GET bookmarks with false user id',async () => {
        const response = await request(app)
          .get(baseApiUrlUnderTest + 'false_user_id/bookmarks')
          .set('Authorization', bearerToken);

        expect(response.status).to.equal(HttpStatus.UNAUTHORIZED);
    });