【问题标题】:Unit testing angular-bootstrap $modal单元测试 angular-bootstrap $modal
【发布时间】:2015-01-07 08:03:03
【问题描述】:

我在尝试为 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);
        });



    });

【问题讨论】:

    标签: javascript angularjs unit-testing jasmine angular-ui


    【解决方案1】:

    我不确定现在这是否对您有所帮助,但是当您监视某些内容时,您可以获得传递给 $uibModal.open 间谍的参数,然后您可以调用该函数来测试它是否返回了解决方法。

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

    *****我的代码使用的是打字稿,但这对我有用。**

    【讨论】:

      【解决方案2】:

      我也遇到过同样的情况。我遇到了以下给定解决方案的问题

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

      如果您想快速修复,希望这可能会有所帮助。

      【讨论】:

        【解决方案3】:

        试试这个:

        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 比较函数的方式。看看这个fiddle。无论如何,我不关心这个,因为我们不应该期望函数实现。在这种情况下,我们关心的是 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.
        

        【讨论】:

        • 如此简单!非常感谢。这困扰了我一段时间。
        【解决方案4】:

        这是一个pass by reference vs pass by value 问题。 $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)
        }
        

        如果必须测试 resolve 函数,则应将其公开在可以在测试中获得对同一函数的引用的地方。

        【讨论】:

        • 感谢您的回答,但是将我的测试更改为使用 employee: jasmine.any(Function) 或仅使用 employee: Function 仍然没有通过测试
        • 您的解析功能是否使用了缩小保护技术?我的是阻止 jasmine.any(Function) 工作
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-02-08
        • 2017-05-05
        • 1970-01-01
        • 2020-08-08
        • 2017-04-04
        • 2019-03-24
        • 2016-12-29
        相关资源
        最近更新 更多