对于这个问题,这是一个后续问题:Mocking $modal in AngularJS unit tests
引用的SO是一个非常有用的答案的优秀问题。之后我留下的问题是:我如何对模态实例控制器进行单元测试?在引用的SO中,测试调用控制器,但模拟了模态实例控制器。可以说后者也应该进行测试,但事实证明这非常棘手。原因如下:
我将在这里复制引用的SO中的相同示例:
.controller('ModalInstanceCtrl', function($scope, $modalInstance, items){
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
所以我的第一个想法是,我只是直接在测试中实例化控制器,就像任何其他被测控制器一样:
beforeEach(inject(function($rootScope) {
scope = $rootScope.$new();
ctrl = $controller('ModalInstanceCtrl', {$scope: scope});
});
这不起作用,因为在这种情况下,angular没有提供者来注入$ modalInstance,因为它是由UI模式提供的。
接下来,我转向计划B:使用$ modal.open来实例化控制器。这将按预期运行:
beforeEach(inject(function($rootScope, $modal) {
scope = $rootScope.$new();
modalInstance = $modal.open({
template: '<html></html>',
controller: 'ModalInstanceCtrl',
scope: scope
});
});
(注意,模板不能是空字符串,否则会以密码方式失败。)
现在的问题是我无法看到范围,这是单元测试资源收集等的惯用方法。在我的实际代码中,控制器调用资源服务来填充选择列表;我尝试测试这个设置expectGet以满足我的控制器正在使用的服务,我想验证控制器是否将结果放在其范围内。但模式是为模态实例控制器创建一个 new 范围(使用我作为原型传入的范围),我无法弄清楚如何获得该范围的漏洞。 modalInstance对象没有进入控制器的窗口。
有关“正确”测试方法的任何建议吗?
(N.B。:为模态实例控制器创建衍生作用域的行为并不是意料之外的 - 它是记录在案的行为。无论如何测试它的问题仍然有效。)
答案 0 :(得分:31)
我通过直接实例化控制器来测试模态对话框中使用的控制器(就像你最初想的那样)。
由于没有$modalInstance
的模拟版本,我只是创建一个模拟对象并将其传递给控制器。
var modalInstance = { close: function() {}, dismiss: function() {} };
var items = []; // whatever...
beforeEach(inject(function($rootScope) {
scope = $rootScope.$new();
ctrl = $controller('ModalInstanceCtrl', {
$scope: scope,
$modalInstance: modalInstance,
items: items
});
}));
现在控制器的依赖性得到满足,您可以像任何其他控制器一样测试此控制器。
例如,我可以执行spyOn(modalInstance, 'close')
然后断言我的控制器在适当的时候关闭对话框。
答案 1 :(得分:13)
或者,如果您使用的是jasmine,则可以使用$uibModalInstance
方法模拟createSpy
:
beforeEach(inject(function ($controller, $rootScope) {
$scope = $rootScope.$new();
$uibModalInstance = jasmine.createSpyObj('$uibModalInstance', ['close', 'dismiss']);
ModalCtrl = $controller('ModalCtrl', {
$scope: $scope,
$uibModalInstance: $uibModalInstance,
});
}));
在不必为每种方法调用spyOn
的情况下对其进行测试,假设您有两种范围方法,cancel()
和confirm()
:
it('should let the user dismiss the modal', function () {
expect($scope.cancel).toBeDefined();
$scope.cancel();
expect($uibModalInstance.dismiss).toHaveBeenCalled();
});
it('should let the user confirm the modal', function () {
expect($scope.confirm).toBeDefined();
$scope.confirm();
expect($uibModalInstance.close).toHaveBeenCalled();
});
答案 2 :(得分:0)
同样的问题是$ uidModalInstance,您可以用类似的方式解决它:
var uidModalInstance = { close: function() {}, dismiss: function() {} };
$ctrl = $controller('ModalInstanceCtrl', {
$scope: $scope,
$uibModalInstance: uidModalInstance
});
或者如所说@yvesmancera你可以使用jasmine.createSpy方法,比如:
var uidModalInstance = jasmine.createSpyObj('$uibModalInstance', ['close', 'dismiss']);
$ctrl = $controller('ModalInstanceCtrl', {
$scope: $scope,
$uibModalInstance: uidModalInstance
});
答案 3 :(得分:0)
按照以下步骤进行操作:
为ModalInstance定义存根,如下面的
uibModalInstanceStub = {
close: sinon.stub(),
dismiss: sinon.stub()
};
在创建控制器
时传递模态实例存根 function createController() {
return $controller(
ppcConfirmGapModalComponentFullName,
{
$scope: scopeStub,
$uibModalInstance: uibModalInstanceStub
});
}
});
存根方法close(),dismiss()将作为测试的一部分被调用
它(&#39;确认模态 - 验证确认操作,在ok()调用调用modalInstance close()函数&#39;,function(){ action =&#39; Ok&#39 ;; scopeStub.item = testItem; createController(); scopeStub.ok(); });