如何测试API端点以获取可由多个代码路径触发的错误路径?

时间:2018-04-07 18:36:16

标签: node.js unit-testing express mocha sinon

假设,函数中有多个路径会导致服务器错误(500)。所以在单元测试中:

  1. 只是从任何一条路径触发错误就足够了吗?
  2. 虽然断言足以断言500错误代码,还是需要断言预期的错误?
  3. 我的意思(在代码中)?

    it('should trigger server error', async function() {
    
      // arrange
      const res = { 
        status: this.sandbox.stub().returnsThis(),
        json: this.sandbox.stub(),
      }; 
      const req = {
        body: { name: 'foo', email: 'foo@bar.com', password: 'password' },
      };
      // switch on one possible trigger path that would lead to 500 error
      const expectedError = new Error('Server Error');
      this.sandbox.stub(User, 'createWithPassword').rejects(expectedError);
    
      // act
      await userApi.create(req, res);
    
      // assert
      // is this assertion enough ?
      expect(res.status).to.have.been.calledWith(500);
      // also should this go along with the above? 
      expect(res.json).to.have.been.calledWith(expectedError);
    });
    

2 个答案:

答案 0 :(得分:1)

  1. 不,你应该测试每条路径。您甚至应该使用代码覆盖率工具(例如istanbul)运行单元测试,以确保涵盖所有路径。这完全是关于cyclomatic complexity;你拥有的路径越多,你需要的测试就越多。保持低水平是件好事。

  2. 断言只是错误的类型是可以的,但你最好断言每一个特定的错误,以确保随着时间的推移你没有引入一个错误,使一个条件抛出另一个错误。在这种情况下,您将获得误报测试。

答案 1 :(得分:1)

解耦错误处理和用户控制器/业务逻辑。您的用户控制器不必担心处理错误。

编写可以处理任何错误的单独错误处理中间件:

module.handleErrors = (error, req, res, next) => {
  // Handle error
  // ...

  res.status(500).json()
}

但是现在我们需要一种方法来捕获控制器中发生的任何错误。所以我们需要另一个中间件来捕获错误并将错误转发给上面的错误处理中间件:

module.catchErrors = controllerAction => (req, res, next) => controllerAction(req, res).catch(next)

现在我们可以从全局错误处理程序开始连接中间件:

const express = require('express)
const { handleErrors } = require('./error-middleware)
const { userRoutes } = require('./user-routes)

const app = express()

app.use('/users', userRoutes)
app.use(handleErrors)

// ..

接下来连接用户路由和控制器以捕获错误:

const express = require('express')
const userController = require('./user-controller')
const { catchErrors } = require('./error-middleware')

const router = express.Router()

// Passing the createWithPassword function to the catch error middleware.
router.post('/', catchErrors(userController.createWithPassword))

module.exports = router

现在所有内容都已解耦,您可以自由地单独测试每个部分以进行单元测试。集成测试是您在许多场景中进行测试的地方。所以:

  1. 通过我描述的上述设置,我们不需要来测试每个场景,因为错误处理中间件最终会捕获任何错误。我们所关心的只是在出现错误时才能正确处理。
  2. 这取决于您的要求。如果您所做的只是抛出没有明确消息/原因的异常,那么检查500并且类型就足够了。但是,如果您希望在特定操作期间发生特定的错误,那么测试错误消息,异常,HTTP状态等将是测试。< / LI>