对结束承诺链的函数进行单元测试
Unit test a function that ends the promise chain
假设我在一个名为 UserController 的 class 中有一个函数,它按照这些行做一些事情(其中 userService.createUser()
returns 一个承诺):
function createUser(req, res)
{
const userInfo = req.body;
userService.createUser(userInfo)
.then(function(){res.json({message: "User added successfully"})})
.fail(function(error){res.send(error)})
.done();
}
我如何测试,当承诺解决时,调用 res.json()
,当承诺拒绝时,调用 res.send(error)
?
我试过写这样的测试:
const userService = ...
const userController = new UserController(userService);
const response = {send: sinon.stub()};
...
const anError = new Error();
userService.createUser = sinon.stub().returns(Q.reject(anError));
userController.createUser(request, response);
expect(response.send).to.be.calledWith(anError);
但是测试失败 "response.send is never called"。我还尝试在调用 res.send(error)
之前记录一些东西,并且记录确实发生了。
我的猜测是 expect()
在执行 res.send(error)
之前被调用,因为它是异步的。
我对 promises 和单元测试还很陌生,这是我的架构问题还是我对 promises 的使用?
我将 Q 用于 promises,将 mocha、chai、sinon 用于我的单元测试。
当你有一个异步调用时,expect
语句在 userController.createUser()
行之后被调用。所以当评估断言时它还没有被调用。
要异步测试您的代码,您需要在 it
语句中声明 done
,然后手动调用它以获取结果。
在你的测试文件中:
it('should work', function(done) {
...
userController.createUser(request, response);
process.nextTick(function(){
expect(response.send).to.be.calledWith(anError);
done();
});
});
这将使 Mocha(我假设您正在使用它)在调用 done()
时评估您的 excpect
。
或者,您可以在 UserController.createUser
函数上设置一个 cb 函数,然后在 .done()
上调用它:
用户控制器
function createUser(req, res, cb) {
const userInfo = req.body;
userService.createUser(userInfo)
.then(function(){res.json({message: "User added successfully"})})
.fail(function(error){res.send(error)})
.done(function(){ if(cb) cb() });
}
然后在你的测试中:
userController.createUser(request, response, function() {
expect(response.send).to.be.calledWith(anError);
done();
});
假设您使用 Mocha 或 Jasmine 作为框架,更简单的方法是从刚开始时继续,但完全跳过 Sinon(因为这里不需要它,除非您测试收到的实际参数):
// observe the `done` callback - calling it signals success
it('should call send on successful service calls', (done) => {
// assuming same code as in question
...
const response = {send: done};
userController.createUser(request, response);
});
// observe the `done` callback - calling it signals success
it('should call send on failing service calls', (done) => {
// assuming same code as in question
...
const response = {send: err => err? done(): done(new Error("No error received"))};
userController.createUser(request, response);
});
披露:我是 Sinon 维护团队的一员。
假设我在一个名为 UserController 的 class 中有一个函数,它按照这些行做一些事情(其中 userService.createUser()
returns 一个承诺):
function createUser(req, res)
{
const userInfo = req.body;
userService.createUser(userInfo)
.then(function(){res.json({message: "User added successfully"})})
.fail(function(error){res.send(error)})
.done();
}
我如何测试,当承诺解决时,调用 res.json()
,当承诺拒绝时,调用 res.send(error)
?
我试过写这样的测试:
const userService = ...
const userController = new UserController(userService);
const response = {send: sinon.stub()};
...
const anError = new Error();
userService.createUser = sinon.stub().returns(Q.reject(anError));
userController.createUser(request, response);
expect(response.send).to.be.calledWith(anError);
但是测试失败 "response.send is never called"。我还尝试在调用 res.send(error)
之前记录一些东西,并且记录确实发生了。
我的猜测是 expect()
在执行 res.send(error)
之前被调用,因为它是异步的。
我对 promises 和单元测试还很陌生,这是我的架构问题还是我对 promises 的使用?
我将 Q 用于 promises,将 mocha、chai、sinon 用于我的单元测试。
当你有一个异步调用时,expect
语句在 userController.createUser()
行之后被调用。所以当评估断言时它还没有被调用。
要异步测试您的代码,您需要在 it
语句中声明 done
,然后手动调用它以获取结果。
在你的测试文件中:
it('should work', function(done) {
...
userController.createUser(request, response);
process.nextTick(function(){
expect(response.send).to.be.calledWith(anError);
done();
});
});
这将使 Mocha(我假设您正在使用它)在调用 done()
时评估您的 excpect
。
或者,您可以在 UserController.createUser
函数上设置一个 cb 函数,然后在 .done()
上调用它:
用户控制器
function createUser(req, res, cb) {
const userInfo = req.body;
userService.createUser(userInfo)
.then(function(){res.json({message: "User added successfully"})})
.fail(function(error){res.send(error)})
.done(function(){ if(cb) cb() });
}
然后在你的测试中:
userController.createUser(request, response, function() {
expect(response.send).to.be.calledWith(anError);
done();
});
假设您使用 Mocha 或 Jasmine 作为框架,更简单的方法是从刚开始时继续,但完全跳过 Sinon(因为这里不需要它,除非您测试收到的实际参数):
// observe the `done` callback - calling it signals success
it('should call send on successful service calls', (done) => {
// assuming same code as in question
...
const response = {send: done};
userController.createUser(request, response);
});
// observe the `done` callback - calling it signals success
it('should call send on failing service calls', (done) => {
// assuming same code as in question
...
const response = {send: err => err? done(): done(new Error("No error received"))};
userController.createUser(request, response);
});
披露:我是 Sinon 维护团队的一员。