我为一个控制器写一个单元测试,触发一个$ modal,并使用promise返回执行一些逻辑。我可以测试父控制器触发$模态,但我不能为我的生活搞清楚如何模拟一个成功的承诺。
我尝试了很多方法,包括使用$ q和$ scope。$ apply()强制promise的解决方案。然而,最近我已经得到了一些类似于最后的答案在this SO post;
我看到这个问题几次与“老”$对话框模态。
我不能找到很多关于如何做到与“新的”$对话框模态。
一些指针将被赞赏。
为了说明这个问题我使用example provided in the UI Bootstrap docs,有一些小的编辑。
控制器(主和模态)
- 'use strict';
- angular.module('angularUiModalApp')
- .controller('MainCtrl',function($scope,$modal,$log) {
- $scope.items = ['item1','item2','item3'];
- $scope.open = function() {
- $scope.modalInstance = $modal.open({
- templateUrl: 'myModalContent.html',controller: 'ModalInstanceCtrl',resolve: {
- items: function() {
- return $scope.items;
- }
- }
- });
- $scope.modalInstance.result.then(function(selectedItem) {
- $scope.selected = selectedItem;
- },function() {
- $log.info('Modal dismissed at: ' + new Date());
- });
- };
- })
- .controller('ModalInstanceCtrl',$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');
- };
- });
视图(main.html)
- <div ng-controller="MainCtrl">
- <script type="text/ng-template" id="myModalContent.html">
- <div class="modal-header">
- <h3>I is a modal!</h3>
- </div>
- <div class="modal-body">
- <ul>
- <li ng-repeat="item in items">
- <a ng-click="selected.item = item">{{ item }}</a>
- </li>
- </ul>
- Selected: <b>{{ selected.item }}</b>
- </div>
- <div class="modal-footer">
- <button class="btn btn-primary" ng-click="ok()">OK</button>
- <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
- </div>
- </script>
- <button class="btn btn-default" ng-click="open()">Open me!</button>
- <div ng-show="selected">Selection from a modal: {{ selected }}</div>
- </div>
考试
- 'use strict';
- describe('Controller: MainCtrl',function() {
- // load the controller's module
- beforeEach(module('angularUiModalApp'));
- var MainCtrl,scope;
- var fakeModal = {
- open: function() {
- return {
- result: {
- then: function(callback) {
- callback("item1");
- }
- }
- };
- }
- };
- beforeEach(inject(function($modal) {
- spyOn($modal,'open').andReturn(fakeModal);
- }));
- // Initialize the controller and a mock scope
- beforeEach(inject(function($controller,$rootScope,_$modal_) {
- scope = $rootScope.$new();
- MainCtrl = $controller('MainCtrl',{
- $scope: scope,$modal: _$modal_
- });
- }));
- it('should show success when modal login returns success response',function() {
- expect(scope.items).toEqual(['item1','item3']);
- // Mock out the modal closing,resolving with a selected item,say 1
- scope.open(); // Open the modal
- scope.modalInstance.close('item1');
- expect(scope.selected).toEqual('item1');
- // No dice (scope.selected) is not defined according to Jasmine.
- });
- });
当你在beforeEach中监听$ modal.open函数时,
- spyOn($modal,'open').andReturn(fakeModal);
- or
- spyOn($modal,'open').and.returnValue(fakeModal); //For Jasmine 2.0+
你需要返回一个模拟$ modal.open通常返回的模拟,而不是$ modal的模拟,它不包括一个打开的功能,你在你的fakeModal模拟。假模态必须有一个结果对象,它包含一个then函数来存储回调(单击OK或Cancel按钮时调用)。它还需要一个关闭功能(模拟确定按钮点击模态)和一个关闭功能(模拟取消按钮点击模态)。 close和dismiss函数在调用时调用必要的回调函数。
将fakeModal更改为以下内容,单元测试将通过:
- var fakeModal = {
- result: {
- then: function(confirmCallback,cancelCallback) {
- //Store the callbacks for later when the user clicks on the OK or Cancel button of the dialog
- this.confirmCallBack = confirmCallback;
- this.cancelCallback = cancelCallback;
- }
- },close: function( item ) {
- //The user clicked OK on the modal dialog,call the stored confirm callback with the selected item
- this.result.confirmCallBack( item );
- },dismiss: function( type ) {
- //The user clicked cancel on the modal dialog,call the stored cancel callback
- this.result.cancelCallback( type );
- }
- };
此外,您可以通过在取消处理程序中添加一个要测试的属性来测试取消对话框,在本例中为$ scope.canceled:
- $scope.modalInstance.result.then(function (selectedItem) {
- $scope.selected = selectedItem;
- },function () {
- $scope.canceled = true; //Mark the modal as canceled
- $log.info('Modal dismissed at: ' + new Date());
- });
一旦设置了取消标志,单元测试将如下所示:
- it("should cancel the dialog when dismiss is called,and $scope.canceled should be true",function () {
- expect( scope.canceled ).toBeUndefined();
- scope.open(); // Open the modal
- scope.modalInstance.dismiss( "cancel" ); //Call dismiss (simulating clicking the cancel button on the modal)
- expect( scope.canceled ).toBe( true );
- });