javascript 测试 AngularUI Bootstrap 模态实例控制器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24373220/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 02:39:56  来源:igfitidea点击:

Testing AngularUI Bootstrap modal instance controller

javascriptangularjsunit-testingangular-ui

提问by David Pisoni

This is a somewhat of a follow-on question to this one: Mocking $modal in AngularJS unit tests

这是这个问题的一个后续问题:Mocking $modal in AngularJS unit tests

The referenced SO is an excellent question with very useful answer. The question I am left with after this however is this: how do I unit test the modal instance controller? In the referenced SO, the invoking controller is tested, but the modal instance controller is mocked. Arguably the latter should also be tested, but this has proven to be very tricky. Here's why:

引用的 SO 是一个很好的问题,具有非常有用的答案。然而,在此之后我留下的问题是:如何对模态实例控制器进行单元测试?在引用的 SO 中,测试了调用控制器,但模拟了模态实例控制器。可以说后者也应该被测试,但事实证明这是非常棘手的。原因如下:

I'll copy the same example from the referenced SO here:

我将从此处引用的 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');
  };
});

So my first thought was that I would just instantiate the controller directly in a test, just like any other controller under test:

所以我的第一个想法是我会直接在测试中实例化控制器,就像任何其他被测控制器一样:

beforeEach(inject(function($rootScope) {
  scope = $rootScope.$new();
  ctrl = $controller('ModalInstanceCtrl', {$scope: scope});
});

This does not work because in this context, angular does not have a provider to inject $modalInstance, since that is supplied by the UI modal.

这不起作用,因为在这种情况下,angular 没有注入 $modalInstance 的提供程序,因为它是由 UI 模式提供的。

Next, I turn to plan B: use $modal.open to instantiate the controller. This will run as expected:

接下来,我转向B计划:使用$modal.open来实例化控制器。这将按预期运行:

beforeEach(inject(function($rootScope, $modal) {
  scope = $rootScope.$new();
  modalInstance = $modal.open({
    template: '<html></html>',
    controller: 'ModalInstanceCtrl',
    scope: scope
  });
});

(Note, template can't be an empty string or it will fail cryptically.)

(注意,模板不能是空字符串,否则会神秘地失败。)

The problem now is that I have no visibility into the scope, which is the customary way to unit test resource gathering, etc. In my real code, the controller calls a resource service to populate a list of choices; my attempt to test this sets an expectGet to satisfy the service my controller is using, and I want to validate that the controller is putting the result in its scope. But the modal is creating a newscope for the modal instance controller (using the scope I pass in as a prototype), and I can't figure out how I can get a hole of that scope. The modalInstance object does not have a window into the controller.

现在的问题是我对范围没有可见性,这是​​对资源收集等进行单元测试的惯用方式。在我的实际代码中,控制器调用资源服务来填充选择列表;我试图测试这个设置了一个 expectGet 来满足我的控制器正在使用的服务,我想验证控制器是否将结果放在其范围内。但是模态正在为模态实例控制器创建一个新的范围(使用我作为原型传入的范围),我无法弄清楚如何获得该范围的漏洞。modalInstance 对象没有进入控制器的窗口。

Any suggestions on the "right" way to test this?

关于测试这个“正确”方法的任何建议?

(N.B.: the behavior of creating a derivative scope for the modal instance controller is not unexpected –?it is documented behavior. My question of how to test it is still valid regardless.)

(注意:为模态实例控制器创建派生范围的行为并不意外——?这是记录在案的行为。无论如何,我关于如何测试它的问题仍然有效。)

回答by Sunil D.

I test the controllers used in modal dialogs by instantiating the controller directly (the same way you initially thought to do it above).

我通过直接实例化控制器来测试在模态对话框中使用的控制器(与您最初想到的方法相同)。

Since there there's no mocked version of $modalInstance, I simply create a mock object and pass that into the controller.

由于没有$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
  });
}));

Now the dependencies for the controller are satisfied and you can test this controller like any other controller.

现在控制器的依赖关系已经满足,你可以像测试任何其他控制器一样测试这个控制器。

For example, I can do spyOn(modalInstance, 'close')and then assert that my controller is closing the dialog at the appropriate time.

例如,我可以执行spyOn(modalInstance, 'close')然后断言我的控制器在适当的时间关闭对话框。

回答by yvesmancera

Alternatively, if you're using jasmine, you can mock the $uibModalInstanceusing the createSpymethod:

或者,如果您使用的是茉莉花,则可以$uibModalInstance使用以下createSpy方法模拟:

beforeEach(inject(function ($controller, $rootScope) {
  $scope = $rootScope.$new();
  $uibModalInstance = jasmine.createSpyObj('$uibModalInstance', ['close', 'dismiss']);

  ModalCtrl = $controller('ModalCtrl', {
    $scope: $scope,
    $uibModalInstance: $uibModalInstance,
  });
}));

And test it without having to call spyOnon each method, let's say you have 2 scope methods, cancel()and confirm():

无需调用spyOn每个方法即可对其进行测试,假设您有 2 个作用域方法,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();
});

回答by kris_IV

The same problem is with $uidModalInstance and you can solve it in similar way:

$uidModalInstance 也有同样的问题,你可以用类似的方式解决它:

var uidModalInstance = { close: function() {}, dismiss: function() {} };

$ctrl = $controller('ModalInstanceCtrl', {
   $scope: $scope,
   $uibModalInstance: uidModalInstance
});

or as said @yvesmancera you can use jasmine.createSpy method instead, like:

或者如@yvesmancera 所说,您可以改用 jasmine.createSpy 方法,例如:

var uidModalInstance = jasmine.createSpyObj('$uibModalInstance', ['close', 'dismiss']);

$ctrl = $controller('ModalInstanceCtrl', {
   $scope: $scope,
   $uibModalInstance: uidModalInstance
});

回答by Dilip Nannaware

Follow below given steps:

按照以下给出的步骤:

  • Define stub for ModalInstance like give below

            uibModalInstanceStub = {
                close: sinon.stub(),
                dismiss: sinon.stub()
            };
    
  • Pass the modal instance stub while creating controller

        function createController() {
            return $controller(
                ppcConfirmGapModalComponentFullName,
                {
                    $scope: scopeStub,
                    $uibModalInstance: uibModalInstanceStub
                });
        }
    });
    
  • Stub methods close(), dismiss() will get called as part of the tests

    it('confirm modal - verify confirm action, on ok() call calls modalInstance close() function', function() { action = 'Ok'; scopeStub.item = testItem; createController(); scopeStub.ok(); });

  • 为 ModalInstance 定义存根,如下所示

            uibModalInstanceStub = {
                close: sinon.stub(),
                dismiss: sinon.stub()
            };
    
  • 创建控制器时传递模态实例存根

        function createController() {
            return $controller(
                ppcConfirmGapModalComponentFullName,
                {
                    $scope: scopeStub,
                    $uibModalInstance: uibModalInstanceStub
                });
        }
    });
    
  • 存根方法 close()、dismiss() 将作为测试的一部分被调用

    it('confirm modal - 验证确认动作, on ok() 调用调用 modalInstance close() 函数', function() { action = 'Ok'; scopeStub.item = testItem; createController(); scopeStub.ok(); } );