使用bootstrap ui对话框对Angular js进行单元测试

Unit Testing Angular js with bootstrap-ui dialog

本文关键字:js 单元测试 Angular bootstrap ui 对话框 使用      更新时间:2023-09-26

我很难理解如何使用jasmine来单元测试angularjs中bootstrap ui对话框元素的创建。控制器:

MyModule.controller('BaseCtrl', ['$scope', '$routeParams', '$location', '$http', '$filter', '$data', '$locationParse', '$dialog', function ($scope, $routeParams, $location, $http, $filter, $data, $locationParse, $dialog) {
//[...Loads of other stuff...]
//method in question:
    $scope.delete = function() {
        var boxResult;
        if ($scope.record._id) {
            var msgBox = $dialog.messageBox('Delete Item', 'Are you sure you want to delete this record?', [{
            label: 'Yes',
            result: 'yes'
        }, {
            label: 'No',
            result: 'no'
        }]);
        msgBox.open()
        .then(function(result) {
            if (result === 'yes') {
                $http.delete('api/' + $scope.modelName + '/' + $scope.id).success(function() {
                        if (typeof $scope.dataEventFunctions.onAfterDelete === "function") {
                            $scope.dataEventFunctions.onAfterDelete(master);
                        }
                        $location.path('/' + $scope.modelName);
                    });
                }
                if (result === 'no') {
                    boxResult = result;
                };
        });
            //can't close the msxBox from within itself as it breaks it. OK for now. TODO Refactor.
            if (boxResult === 'no') {
                msgBox.close();
            }
        }
    }
}]);

测试对象:

describe('"BaseCtrl"', function(){
var $httpBackend;
beforeEach(function() {
    angular.mock.module('MyModule');
});
afterEach(function() {
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();
});
describe('deletion confirmation modal', function() {
    var $scope, ctrl, $dialog, fakeDialog;

    beforeEach(function() {
        inject(function(_$httpBackend_, $rootScope, $routeParams, $controller, $location, _$dialog_){
             $dialog = _$dialog_;

            fakeDialog = function (title, msg, btns) {
                return {
                    open: function () {
                        return {
                             then: function (callback) {
                                  callback('ok'); // 'ok' will be set to param result
                             }
                         }
                    }
                }
             };


            $httpBackend = _$httpBackend_;
            $httpBackend.whenGET('api/schema/collection').respond({"email":{"enumValues":[],"regExp":null,"path":"email","instance":"String","validators":[],"setters":[],"getters":[],"options":{"form":{"directive":"email-field"}},"_index":null,"$conditionalHandlers":{}}});
            $location.$$path = '/collection/new';
            $scope = $rootScope.$new();
            ctrl = $controller("BaseCtrl", {$scope: $scope, $dialog: $dialog});
            $httpBackend.flush();
            spyOn($dialog, 'messageBox').andReturn(fakeDialog);

        });
    });
    it('should inject bootstrap-ui dialog controller', function() {
         expect($dialog).toBeDefined();

    });

    it('should be displayed when $scope.delete() is called', function() {
        $scope.record._id = 1;
         $scope.delete();
        // console.log(dialog.messageBox);
        // expect(dialog.open).toHaveBeenCalled();
    });
});
});

我得到错误:

PhantomJS 1.9 (Mac) "BaseCtrl" deletion confirmation modal should be displayed when $scope.delete() is called FAILED
TypeError: 'undefined' is not a function (evaluating 'msgBox.open()')
    at /Users/steveclements/work/live/forms-angular/app/js/controllers/base.js:632
    at /Users/steveclements/work/live/forms-angular/test/unit/baseControllerSpec.js:735

如果我删除fakeDialog方法(和其他相关的测试代码),我会得到错误:

PhantomJS 1.9 (Mac) "BaseCtrl" deletion confirmation modal should be displayed when $scope.delete() is called FAILED
TypeError: 'undefined' is not an object (evaluating 'msgBox.open')
    at /Users/steveclements/work/live/forms-angular/app/js/controllers/base.js:632
    at /Users/steveclements/work/live/forms-angular/test/unit/baseControllerSpec.js:735

区别在于"msgBox.open"answers"msgBox.open()",所以我不认为mock是问题所在。我读了很多关于其他SO与此相关的答案,但看不出我错在哪里。

我将测试代码更改为:

describe('deletion confirmation modal', function() {
    var $scope, ctrl, $dialog, fakeDialog, provider, resolveCallback;
    //this fake object replaces the actual dialog object, as the functions are not visible to the test runner.
    fakeDialog = {
        isOpen: false,
        open: function() {
            fakeDialog.isOpen = true;
            return {
                then: resolveCallback
            };
        },
        close: function() {
            return true;
        }
    };
    resolveCallback = function(callback) {
            }
    beforeEach(function() {
        module(function($dialogProvider) {
            provider = $dialogProvider;
        });
        inject(function(_$httpBackend_, $rootScope, $routeParams, $controller, $location, _$dialog_) {
            $dialog = _$dialog_;
            $httpBackend = _$httpBackend_;
            $httpBackend.whenGET('api/schema/collection').respond({
                "email": {
                    "enumValues": [],
                    "regExp": null,
                    "path": "email",
                    "instance": "String",
                    "validators": [],
                    "setters": [],
                    "getters": [],
                    "options": {
                        "form": {
                            "directive": "email-field"
                        }
                    },
                    "_index": null,
                    "$conditionalHandlers": {}
                }
            });
            $location.$$path = '/collection/new';
            $scope = $rootScope.$new();
            ctrl = $controller("BaseCtrl", {
                $scope: $scope,
                $dialog: $dialog
            });
            $httpBackend.flush();
            spyOn($dialog, 'messageBox').andReturn(fakeDialog);
            $scope.record._id = 1;

        });
    });
    it('provider service should be injected', function() {
        expect(provider).toBeDefined();
    });
    it('dialog service should be injected', function() {
        expect($dialog).toBeDefined();
    });
    it('dialog messageBox should be defined', function() {
        $scope.delete();
        expect($dialog.messageBox).toHaveBeenCalled();

    });

    it('should be displayed when $scope.delete() is called', function() {
        $scope.delete();
        expect(fakeDialog.isOpen).toEqual(true);
    });
});