如何使用 Sinon 测试在承诺中设置的 AngularJS 控制器值
How to test an AngularJS controller value that is set within a promise using Sinon
我在测试在服务返回的承诺中设置的控制器值时遇到麻烦。我正在使用 Sinon 对服务进行存根(Karma 用于 运行 测试,Mocha 作为框架,Chai 用于断言)。
与理解问题相比,我对快速修复更感兴趣。我已经阅读了很多,并且在代码和测试下方有一些笔记。
这是代码。
.controller('NavCtrl', function (NavService) {
var vm = this;
NavService.getNav()
.then(function(response){
vm.nav = response.data;
});
})
.service('NavService', ['$http', function ($http) {
this.getNav = function () {
return $http.get('_routes');
};
}]);
这是测试:
describe('NavCtrl', function () {
var scope;
var controller;
var NavService;
var $q;
beforeEach(module('nav'));
beforeEach(inject(function($rootScope, $controller, _$q_, _NavService_){
NavService = _NavService_;
scope = $rootScope.$new();
controller = $controller;
}));
it('should have some data', function () {
var stub = sinon.stub(NavService, 'getNav').returns($q.when({
response: {
data: 'data'
}
}));
var vm = controller("NavCtrl", {
$scope: scope,
NavService: NavService
});
scope.$apply();
stub.callCount.should.equal(1);
vm.should.be.defined;
vm.nav.should.be.defined;
});
});
正在调用存根,即测试通过,并且定义了 vm
,但是 vm.nav
从未获取数据并且测试失败。我认为,我如何处理残存的承诺是罪魁祸首。一些注意事项:
基于reading elsewhere, I'm calling scope.$apply
to set the value, but since scope isn't injected into the original controller, I'm not positive that will do the trick. This article points to the angular docs on $q.
另一篇文章推荐 using $timeout 和 "actually complete the promise" 一样。这篇文章还建议使用 "sinon-as-promised," 我在上面没有做的事情。我试过了,但没看出区别。
这个 Stack Overflow 答案使用 scope.$root.$digest() 因为 "If your scope object's value comes from the promise result, you will need to call scope.$root.$digest()"。但同样,同样的测试失败。再一次,这可能是因为我没有使用范围。
至于stubing the promise,我也试过sinon sandbox way,结果一样。
我已经尝试使用 $scope
重写测试,以确保它不是 vm
style 的问题,但测试仍然失败。
最后,我可能是错的:存根和 promise 可能不是问题,它是不同的东西 and/or 很明显我错过了。
非常感谢任何帮助,如果我可以澄清以上任何内容,请告诉我。
抱歉,您只需要快速修复即可:
var stub = sinon.stub(NavService, 'getNav').returns($q.when({
response: {
data: 'data'
}
}));
您的承诺已解析为包含 response.data
的对象,而不仅仅是 data
检查根据您的代码创建的这个 plunk:https://plnkr.co/edit/GL1Xuf?p=preview
扩展答案
我也经常落入同样的陷阱。于是我开始单独定义一个方法返回的结果。然后,如果该方法是异步的,我将此结果包装在像 $q.when(stubbedResult)
这样的承诺中,这使我可以轻松地 运行 对实际结果的期望,因为我将存根结果保存在变量中,例如
it('Controller should have some data', function () {
var result = {data: 'data'};
var stub = sinon.stub(NavService, 'getNav').returns($q.when(result));
var vm = controller(/* initController */);
scope.$apply();
stub.callCount.should.equal(1);
vm.nav.should.equal(result.data)
})
另外一些测试调试技巧也会派上用场。最简单的方法是在控制台上转储一些数据,以检查某处返回的内容。当然最好使用实际的调试器。
如何快速发现这些错误:
- 在
$rootScope.apply()
行放置一个断点(就在执行之前)
- 在控制器的
NavService.getNav().then
处理程序中放置一个断点,以查看它是否被调用以及调用的内容
- 继续使用调试器执行
$rootScope.$apply()
行。现在调试器应该会命中上一步设置的断点 - 就是这样。
我认为你应该使用 chai-as-promised
然后断言像
这样的承诺
doSomethingAsync().should.eventually.equal("foo");
或者使用异步等待
it('should have some data', async function () {
await scope.$apply();
});
您可能需要移动然后 getNav()
调用 init
有点函数,然后针对该 init
函数进行测试
我在测试在服务返回的承诺中设置的控制器值时遇到麻烦。我正在使用 Sinon 对服务进行存根(Karma 用于 运行 测试,Mocha 作为框架,Chai 用于断言)。
与理解问题相比,我对快速修复更感兴趣。我已经阅读了很多,并且在代码和测试下方有一些笔记。
这是代码。
.controller('NavCtrl', function (NavService) {
var vm = this;
NavService.getNav()
.then(function(response){
vm.nav = response.data;
});
})
.service('NavService', ['$http', function ($http) {
this.getNav = function () {
return $http.get('_routes');
};
}]);
这是测试:
describe('NavCtrl', function () {
var scope;
var controller;
var NavService;
var $q;
beforeEach(module('nav'));
beforeEach(inject(function($rootScope, $controller, _$q_, _NavService_){
NavService = _NavService_;
scope = $rootScope.$new();
controller = $controller;
}));
it('should have some data', function () {
var stub = sinon.stub(NavService, 'getNav').returns($q.when({
response: {
data: 'data'
}
}));
var vm = controller("NavCtrl", {
$scope: scope,
NavService: NavService
});
scope.$apply();
stub.callCount.should.equal(1);
vm.should.be.defined;
vm.nav.should.be.defined;
});
});
正在调用存根,即测试通过,并且定义了 vm
,但是 vm.nav
从未获取数据并且测试失败。我认为,我如何处理残存的承诺是罪魁祸首。一些注意事项:
基于reading elsewhere, I'm calling
scope.$apply
to set the value, but since scope isn't injected into the original controller, I'm not positive that will do the trick. This article points to the angular docs on $q.另一篇文章推荐 using $timeout 和 "actually complete the promise" 一样。这篇文章还建议使用 "sinon-as-promised," 我在上面没有做的事情。我试过了,但没看出区别。
这个 Stack Overflow 答案使用 scope.$root.$digest() 因为 "If your scope object's value comes from the promise result, you will need to call scope.$root.$digest()"。但同样,同样的测试失败。再一次,这可能是因为我没有使用范围。
至于stubing the promise,我也试过sinon sandbox way,结果一样。
我已经尝试使用
$scope
重写测试,以确保它不是vm
style 的问题,但测试仍然失败。
最后,我可能是错的:存根和 promise 可能不是问题,它是不同的东西 and/or 很明显我错过了。
非常感谢任何帮助,如果我可以澄清以上任何内容,请告诉我。
抱歉,您只需要快速修复即可:
var stub = sinon.stub(NavService, 'getNav').returns($q.when({
response: {
data: 'data'
}
}));
您的承诺已解析为包含 response.data
的对象,而不仅仅是 data
检查根据您的代码创建的这个 plunk:https://plnkr.co/edit/GL1Xuf?p=preview
扩展答案
我也经常落入同样的陷阱。于是我开始单独定义一个方法返回的结果。然后,如果该方法是异步的,我将此结果包装在像 $q.when(stubbedResult)
这样的承诺中,这使我可以轻松地 运行 对实际结果的期望,因为我将存根结果保存在变量中,例如
it('Controller should have some data', function () {
var result = {data: 'data'};
var stub = sinon.stub(NavService, 'getNav').returns($q.when(result));
var vm = controller(/* initController */);
scope.$apply();
stub.callCount.should.equal(1);
vm.nav.should.equal(result.data)
})
另外一些测试调试技巧也会派上用场。最简单的方法是在控制台上转储一些数据,以检查某处返回的内容。当然最好使用实际的调试器。
如何快速发现这些错误:
- 在
$rootScope.apply()
行放置一个断点(就在执行之前)
- 在
- 在控制器的
NavService.getNav().then
处理程序中放置一个断点,以查看它是否被调用以及调用的内容
- 在控制器的
- 继续使用调试器执行
$rootScope.$apply()
行。现在调试器应该会命中上一步设置的断点 - 就是这样。
- 继续使用调试器执行
我认为你应该使用 chai-as-promised
然后断言像
这样的承诺 doSomethingAsync().should.eventually.equal("foo");
或者使用异步等待
it('should have some data', async function () {
await scope.$apply();
});
您可能需要移动然后 getNav()
调用 init
有点函数,然后针对该 init
函数进行测试