单元测试角度引导$modal

Unit testing angular-bootstrap $modal

本文关键字:modal 单元测试      更新时间:2023-09-26

我在为Angular Bootstrap $modal编写jasmine单元测试时遇到问题。确切的错误是Expected spy open to have been called with [ { templateUrl : '/n/views/consent.html', controller : 'W2ConsentModal as w2modal', resolve : { employee : Function }, size : 'lg' } ] but actual calls were [ { templateUrl : '/n/views/consent.html', controller : 'W2ConsentModal as w2modal', resolve : { employee : Function }, size : 'lg' } ]

预期的和实际的模态选项对象是相同的。发生了什么事?

控制器

(function () {
    'use strict';
    angular
        .module('app')
        .controller('W2History', W2History);
    W2History.$inject = ['$scope', '$modal', 'w2Service'];
    function W2History($scope, $modal, w2Service) {
        /* jshint validthis:true */
        var vm = this;
        vm.showModal = showModal;
        function showModal(employee) {
            var modalInstance = $modal.open({
                templateUrl: '/n/views/consent.html',
                controller: 'W2ConsentModal as w2modal',
                resolve: {
                    employee: function () {
                        return employee;
                    }
                },
                size: 'lg'
            });
            modalInstance.result.then(function (didConsent) {
                // code omitted
            });
        }

    }
})();

测试

 describe('W2History controller', function () {
        var controller, scope, modal;
        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);
            }
        };
        var modalOptions = {
            templateUrl: '/n/views/consent.html',
            controller: 'W2ConsentModal as w2modal',
            resolve: {
                employee: function () {
                    return employee;
                }
            },
            size: 'lg'
        };
        beforeEach(function () {
            module('app');
            inject(function (_$controller_, _$rootScope_, _$modal_) {
                scope = _$rootScope_.$new();                         
                modal = _$modal_;
                spyOn(modal, 'open').and.returnValue(fakeModal);
                controller = _$controller_('W2History', {
                    $scope: scope,
                    $modal: modal,
                    w2Service: w2Srvc
                });
            });
        });
        it('Should correctly show the W2 consent modal', function () {
            var employee = terminatedaccessMocks.getCurrentUserInfo();
            controller.showModal(employee);
            expect(modal.open).toHaveBeenCalledWith(modalOptions);
        });

    });

试试这个:

describe('W2History controller', function () {
        var controller, scope, modal;
        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);
            }
        };
        var modalOptions = {
            templateUrl: '/n/views/consent.html',
            controller: 'W2ConsentModal as w2modal',
            resolve: {
                employee: jasmine.any(Function)
            },
            size: 'lg'
        };
        var actualOptions;
        beforeEach(function () {
            module('plunker');
            inject(function (_$controller_, _$rootScope_, _$modal_) {
                scope = _$rootScope_.$new();                         
                modal = _$modal_;
                spyOn(modal, 'open').and.callFake(function(options){
                    actualOptions = options;
                    return fakeModal;
                });
                controller = _$controller_('W2History', {
                    $scope: scope,
                    $modal: modal
                });
            });
        });
        it('Should correctly show the W2 consent modal', function () {
            var employee = { name : "test"};
            controller.showModal(employee);
            expect(modal.open).toHaveBeenCalledWith(modalOptions);
            expect(actualOptions.resolve.employee()).toEqual(employee);
        });
    });

PLUNK

解释

我们不应该期望实际的resolve.employee与伪resolve.employee相同,因为resolve.employee是一个返回雇员的函数(在这种情况下,雇员在闭包中被捕获)。函数可能相同,但在运行时返回的对象可能不同。

测试失败的原因是javascript比较函数的方式。看看这把小提琴。无论如何,我不在乎这个,因为我们不应该期望函数实现。在这种情况下,我们关心的是resolve.employee在传入时返回相同的对象:

expect(actualOptions.resolve.employee()).toEqual(employee);

因此,这里的解决方案是:除了resolve.employee:,我们期待一切

var modalOptions = {
                templateUrl: '/n/views/consent.html',
                controller: 'W2ConsentModal as w2modal',
                resolve: {
                    employee: jasmine.any(Function) //don't care about the function as we check it separately.
                },
                size: 'lg'
            };
   expect(modal.open).toHaveBeenCalledWith(modalOptions);

先捕获resolve.employee,单独检查:

var actualOptions;
 spyOn(modal, 'open').and.callFake(function(options){
      actualOptions = options; //capture the actual options               
      return fakeModal;
 });
expect(actualOptions.resolve.employee()).toEqual(employee); //Check the returned employee is actually the one we pass in.

这是一个按引用传递与按值传递的问题。$modal.open:中使用的resolve.employee匿名函数

var modalInstance = $modal.open({
    templateUrl: '/n/views/consent.html',
    controller: 'W2ConsentModal as w2modal',
    resolve: {
        employee: function () {
            return employee;
        }
    },
    size: 'lg'
});

与测试中的resolve.employee匿名函数不相同(通过引用):

var modalOptions = {
    templateUrl: '/n/views/consent.html',
    controller: 'W2ConsentModal as w2modal',
    resolve: {
        employee: function () {
            return employee;
        }
    },
    size: 'lg'
};

你的测试应该是:

resolve: {
    employee: jasmine.any(Function)
}

如果解析函数必须进行测试,那么应该将其公开在某个地方,以便在测试中获得对同一函数的引用。

我不确定这是否会对你有所帮助,但当你监视某个东西时,你可以得到传递给$uibModal.open spy的参数,然后你可以调用该函数来测试它是否返回resolve方法中的内容。

it('expect resolve to be have metadataid that will return 9999', () => {
            spyOn($uibModal, 'open');
            //add test code here that will call the $uibModal.open
            var spy = <jasmine.Spy>$uibModal.open;
            var args = spy.calls.argsFor(0);
            expect(args[0].resolve.metadataId()).toEqual(9999);
});

*****我的代码使用的是typescript,但这对我有效。**

我遇到过同样的场景。我遇到了下面给出的解决方案的问题

//Function to open export modal
scope.openExportModal();
expect( uibModal.open ).toHaveBeenCalledWith(options);
expect( uibModal.open.calls.mostRecent().args[0].resolve.modalData() ).toEqual(modalData);

如果你想快速解决问题,希望这能有所帮助。