首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在AngularJS单元测试中模拟$modal

在AngularJS单元测试中模拟$modal
EN

Stack Overflow用户
提问于 2014-01-19 16:59:34
回答 4查看 39.3K关注 0票数 67

我正在为一个控制器编写一个单元测试,该控制器启动一个$modal并使用返回的promise来执行一些逻辑。我可以测试触发$modal的父控制器,但我无论如何也不能弄清楚如何模拟一个成功的承诺。

我尝试了许多方法,包括使用$q$scope.$apply()来强制解析promise。然而,我得到的最接近的是把类似于this中最后一个答案的东西放在一起,所以post;

我在“老的”$dialog模式中见过几次这样的问题。我找不到太多关于如何使用“新的”$dialog模式来做这件事。

如果有一些指针,我们将非常感谢。

为了说明这个问题,我使用了example provided in the UI Bootstrap docs,做了一些小的修改。

控制器(主和模态)

代码语言:javascript
复制
'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', 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');
        };
    });

查看视图(main.html)

代码语言:javascript
复制
<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>

测试

代码语言:javascript
复制
'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', 'item2', '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.
    });
});
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-01-27 07:31:35

当您监视beforeEach中的$modal.open函数时,

代码语言:javascript
复制
spyOn($modal, 'open').andReturn(fakeModal);

or 

spyOn($modal, 'open').and.returnValue(fakeModal); //For Jasmine 2.0+

您需要返回$modal.open正常返回内容的模拟,而不是$modal的模拟,它不像您在fakeModal模拟中所展示的那样包含open函数。伪模态必须有一个包含then函数的result对象来存储回调(在单击OK或Cancel按钮时调用)。它还需要一个close函数(模拟单击模式上的OK按钮)和一个dismiss函数(模拟单击模式上的Cancel按钮)。closedismiss函数在被调用时调用必要的回调函数。

fakeModal更改为以下内容,单元测试将通过:

代码语言:javascript
复制
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

代码语言:javascript
复制
$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());
});

一旦设置了取消标志,单元测试将如下所示:

代码语言:javascript
复制
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 );
});
票数 91
EN

Stack Overflow用户

发布于 2014-12-15 11:01:46

为了补充Brant的答案,这里有一个略微改进的mock,它可以让你处理其他一些场景。

代码语言:javascript
复制
var fakeModal = {
    result: {
        then: function (confirmCallback, cancelCallback) {
            this.confirmCallBack = confirmCallback;
            this.cancelCallback = cancelCallback;
            return this;
        },
        catch: function (cancelCallback) {
            this.cancelCallback = cancelCallback;
            return this;
        },
        finally: function (finallyCallback) {
            this.finallyCallback = finallyCallback;
            return this;
        }
    },
    close: function (item) {
        this.result.confirmCallBack(item);
    },
    dismiss: function (item) {
        this.result.cancelCallback(item);
    },
    finally: function () {
        this.result.finallyCallback();
    }
};

这将允许mock处理以下情况:

您可以使用具有.then().catch().finally()处理程序样式的模式,而不是将两个函数(successCallback, errorCallback)传递给.then(),例如:

代码语言:javascript
复制
modalInstance
    .result
    .then(function () {
        // close hander
    })
    .catch(function () {
        // dismiss handler
    })
    .finally(function () {
        // finally handler
    });
票数 9
EN

Stack Overflow用户

发布于 2015-08-22 21:26:11

由于modals使用的是promises,因此您绝对应该使用$q来完成这类工作。

代码变成:

代码语言:javascript
复制
function FakeModal(){
    this.resultDeferred = $q.defer();
    this.result = this.resultDeferred.promise;
}
FakeModal.prototype.open = function(options){ return this;  };
FakeModal.prototype.close = function (item) {
    this.resultDeferred.resolve(item);
    $rootScope.$apply(); // Propagate promise resolution to 'then' functions using $apply().
};
FakeModal.prototype.dismiss = function (item) {
    this.resultDeferred.reject(item);
    $rootScope.$apply(); // Propagate promise resolution to 'then' functions using $apply().
};

// ....

// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope) {
    scope = $rootScope.$new();
    fakeModal = new FakeModal();
    MainCtrl = $controller('MainCtrl', {
        $scope: scope,
        $modal: fakeModal
   });
}));

// ....

it("should cancel the dialog when dismiss is called, and  $scope.canceled should be true", function () {
    expect( scope.canceled ).toBeUndefined();

    fakeModal.dismiss( "cancel" ); //Call dismiss (simulating clicking the cancel button on the modal)
    expect( scope.canceled ).toBe( true );
});
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21214868

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档