【问题标题】:How to properly test an Express controller method with Mocha, Chai, and Sinon如何使用 Mocha、Chai 和 Sinon 正确测试 Express 控制器方法
【发布时间】:2018-05-05 01:54:59
【问题描述】:

我对使用诗乃很陌生。我编写了以下测试,但它失败了,因为 res.status 总是以未调用的方式返回。

import chai from 'chai';
import 'chai/register-should';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import { db } from '../../models';
import * as loginController from '../../controllers/login';

chai.use(sinonChai);

describe('Login controller', () => {

  describe('post function', () => {
    let findOne, req, status, send, res;

    beforeEach(() => {
      findOne = sinon.stub(db.User, 'findOne');
      findOne.resolves(null);
      req = { body: { email: 'test@test.com', password: 'testpassword' }};
      status = sinon.stub();
      send = sinon.spy();
      res = { send: send, status: status };
      status.returns(res);
      loginController.post(req, res);
    });
    afterEach(() => {
      findOne.restore();
    });
    it('should return a 401 status for an invalid email', (done) => {
      res.status.should.be.calledWith(401);
      findOne.restore();
      done();
    });

  });
});

现在控制器中的方法非常简单。它首先使用 sequelize findOne 方法。如果它没有找到匹配的电子邮件,它应该抛出 401。如下所示:

export function post(req,res) {
  const email = req.body.email;
  const password = req.body.password;

  db.User.findOne({
    where: {email: email}
  }).then(user => {
    if (user) {
      // Other stuff happens here
    } else {
      res.status(401).send('That email address does not exist in our system.');
    }
  }).catch((error) => {
    res.status(500).send(error.message);
  });
}

当我运行测试时,它确实到达了应该返回状态的 else 语句,但测试失败了,当我检查日志时,它说 res.status 从未被调用过。

【问题讨论】:

    标签: javascript express ecmascript-6 sinon sinon-chai


    【解决方案1】:

    这里的问题是规范是同步的并且没有考虑到承诺。

    出于可测试性的原因返回一个承诺是有意义的:

    export function post(req,res) {
      ...
      return db.User.findOne(...)
      ...
    }
    

    如果路由处理程序是async函数,这自然可以做到。

    由于 Mocha 支持 Promise,规范也可以使用 async 函数而不是 done 回调:

    it('should return a 401 status for an invalid email', async () => {
      const handlerResult = loginController.post(req, res);
      expect(handlerResult).to.be.a('promise');
    
      await handlerResult;
      res.status.should.be.calledWith(401);
    });
    

    【讨论】:

    • 我正在尝试这个,但它不喜欢 async 带有匿名函数声明。你确定语法正确吗?错误是:ReferenceError: regeneratorRuntime is not defined
    • 正确。您的设置中存在 Babel 配置问题。谷歌为你的错误。
    • 是的。需要 babel-polyfill
    • 我大部分时间都在工作。唯一的问题是我使用应该而不是期望,但我尝试了两种方式。 expect(handlerResult).go.be.a('promise') and handlerResult.should.be.a('promise')` 都出错了。我得到Cannot read property 'should' of undefined
    • 您可能可以在存根上使用should,因为您使用的是github.com/domenic/sinon-chai。您不会在随机值上获得 should 属性 - 因为其中一些不能有道具,例如undefinednull。它是undefined 的事实表明您没有从post 返回一个承诺(这个断言正是为了测试这一点)。
    猜你喜欢
    • 2021-01-01
    • 2020-10-11
    • 2015-12-01
    • 2015-11-06
    • 1970-01-01
    • 2017-10-08
    • 2014-12-21
    • 1970-01-01
    相关资源
    最近更新 更多