在 AngularJS 单元测试中模拟 $modal [英] Mocking $modal in AngularJS unit tests

查看:30
本文介绍了在 AngularJS 单元测试中模拟 $modal的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我尝试了多种方法,包括使用 $q$scope.$apply() 来强制解析 promise.但是,我得到的最接近的是将类似于 this SO post;

我已经用旧"$dialog 模态多次看到这个问题.我找不到太多关于如何使用新"$dialog 模态来做到这一点的信息.

一些指针将不胜感激.

为了说明问题,我使用了 UI Bootstrap 文档中提供的 示例,稍作修改.

控制器(主要和模态)

'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',控制器:'ModalInstanceCtrl',解决: {项目:函数(){返回 $scope.items;}}});$scope.modalInstance.result.then(function(selectedItem) {$scope.selected = selectedItem;}, 功能() {$log.info('Modal 在以下时间被驳回:' + new Date());});};}).controller('ModalInstanceCtrl', function($scope, $modalInstance, items) {$scope.items = 物品;$scope.selected = {项目:$scope.items[0]};$scope.ok = function() {$modalInstance.close($scope.selected.item);};$scope.cancel = function() {$modalInstance.dismiss('cancel');};});

视图 (main.html)

<script type="text/ng-template" id="myModalContent.html"><div class="modal-header"><h3>I 是一个模态!</h3>

<div class="modal-body"><ul><li ng-repeat="项目中的项目"><a ng-click="selected.item = item">{{ item }}</a>已选择:<b>{{ selected.item }}</b>

<div class="modal-footer"><button class="btn btn-primary" ng-click="ok()">OK</button><button class="btn btn-warning" ng-click="cancel()">取消</button>

<button class="btn btn-default" ng-click="open()">打开我!</button><div ng-show="selected">从模态中选择:{{ selected }}</div>

测试

'use strict';描述('控制器:MainCtrl',函数(){//加载控制器模块beforeEach(module('angularUiModalApp'));var MainCtrl,范围;var fakeModal = {打开:函数(){返回 {结果: {然后:函数(回调){回调(项目1");}}};}};beforeEach(inject(function($modal) {spyOn($modal, 'open').andReturn(fakeModal);}));//初始化控制器和模拟范围beforeEach(inject(function($controller, $rootScope, _$modal_) {范围 = $rootScope.$new();MainCtrl = $controller('MainCtrl', {$范围:范围,$modal: _$modal_});}));it('当模态登录返回成功响应时应该显示成功', function() {期望(scope.items).toEqual(['item1', 'item2', 'item3']);//模拟模态关闭,用选定的项目解析,比如 1范围.open();//打开模态scope.modalInstance.close('item1');期望(范围.选择).toEqual('item1');//No dice (scope.selected) 不是根据 Jasmine 定义的.});});

解决方案

当你窥探 beforeEach 中的 $modal.open 函数时,

spyOn($modal, 'open').andReturn(fakeModal);或者spyOn($modal, 'open').and.returnValue(fakeModal);//对于茉莉花 2.0+

您需要返回 $modal.open 通常返回的模拟,而不是 $modal 的模拟,它不包括您在 fakeModal 中布置的 open 函数 模拟.假模态必须有一个 result 对象,其中包含一个 then 函数来存储回调(在点击 OK 或 Cancel 按钮时调用).它还需要一个 close 函数(模拟在模态上单击 OK 按钮)和一个 dismiss 函数(模拟在模态上单击 Cancel 按钮).closedismiss 函数在调用时调用必要的回调函数.

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

var fakeModal = {结果: {然后:函数(确认回调,取消回调){//当用户点击对话框的确定"或取消"按钮时,存储回调以备后用this.confirmCallBack = confirmCallback;this.cancelCallback = cancelCallback;}},关闭:功能(项目){//用户在模态对话框上点击了OK,使用选中的item调用存储的confirm回调this.result.confirmCallBack( item );},解雇:功能(类型){//用户在模态对话框上点击取消,调用存储的取消回调this.result.cancelCallback(类型);}};

此外,您可以通过在取消处理程序中添加要测试的属性来测试取消对话框的情况,在这种情况下 $scope.canceled:

$scope.modalInstance.result.then(function (selectedItem) {$scope.selected = selectedItem;}, 功能 () {$scope.canceled = true;//将模态标记为取消$log.info('Modal 在以下时间被驳回:' + new Date());});

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

it("调用dismiss时应该取消对话框,$scope.canceled应该为true", function () {期望(范围.取消).toBeUndefined();范围.open();//打开模态scope.modalInstance.dismiss("取消");//调用dismiss(模拟点击modal上的取消按钮)期望(范围.取消).toBe(真);});

I'm writing a unit test for a controller that fires up a $modal and uses the promise returned to execute some logic. I can test the parent controller that fires the $modal, but I can't for the life of me figure out how to mock a successful promise.

I've tried a number of ways, including using $q and $scope.$apply() to force the resolution of the promise. However, the closest I've gotten is putting together something similar to the last answer in this SO post;

I've seen this asked a few times with the "old" $dialog modal. I can't find much on how to do it with the "new" $dialog modal.

Some pointers would be tres appreciated.

To illustrate the problem I'm using the example provided in the UI Bootstrap docs, with some minor edits.

Controllers (Main and Modal)

'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');
        };
    });

The view (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>

The test

'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.
    });
});

解决方案

When you spy on the $modal.open function in the beforeEach,

spyOn($modal, 'open').andReturn(fakeModal);

or 

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

you need to return a mock of what $modal.open normally returns, not a mock of $modal, which doesn’t include an open function as you laid out in your fakeModal mock. The fake modal must have a result object that contains a then function to store the callbacks (to be called when the OK or Cancel buttons are clicked on). It also needs a close function (simulating an OK button click on the modal) and a dismiss function (simulating a Cancel button click on the modal). The close and dismiss functions call the necessary call back functions when called.

Change the fakeModal to the following and the unit test will pass:

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 );
    }
};

Additionally, you can test the cancel dialog case by adding a property to test in the cancel handler, in this case $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());
});

Once the cancel flag is set, the unit test will look something like this:

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 );
});

这篇关于在 AngularJS 单元测试中模拟 $modal的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆