如何使用 Mocha、Chai 和 Sinon 正确测试 Express 控制器方法
How to properly test an Express controller method with Mocha, Chai, and Sinon
我刚开始使用 Sinon。我编写了以下测试,但它失败了,因为 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
不是'从来没有打过电话。
这里的问题是规范是同步的,没有考虑承诺。
出于可测试性原因,return 承诺是有意义的:
export function post(req,res) {
...
return db.User.findOne(...)
...
}
如果路由处理程序是 async
函数,这可以很自然地完成。
由于 Mocha 支持承诺,规范也可以使用 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);
});
我刚开始使用 Sinon。我编写了以下测试,但它失败了,因为 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
不是'从来没有打过电话。
这里的问题是规范是同步的,没有考虑承诺。
出于可测试性原因,return 承诺是有意义的:
export function post(req,res) {
...
return db.User.findOne(...)
...
}
如果路由处理程序是 async
函数,这可以很自然地完成。
由于 Mocha 支持承诺,规范也可以使用 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);
});