angular-ui / bootstrap:测试使用对话框的控制器

时间:2013-03-19 01:48:00

标签: javascript testing angularjs jasmine angular-ui

我有一个控制器,它使用angular-ui / bootstrap中的Dialog:

   function ClientFeatureController($dialog, $scope, ClientFeature, Country, FeatureService) {

        //Get list of client features for selected client (that is set in ClientController)
        $scope.clientFeatures = ClientFeature.query({clientId: $scope.selected.client.id}, function () {
            console.log('getting clientfeatures for clientid: ' + $scope.selected.client.id);
            console.log($scope.clientFeatures);
        });

        //Selected ClientFeature
        $scope.selectedClientFeature = {};

        /**
         * Edit selected clientFeature.
         * @param clientFeature
         */
        $scope.editClientFeature = function (clientFeature) {
            //set selectedClientFeature for data binding
            $scope.selectedClientFeature = clientFeature;

            var dialogOpts = {
                templateUrl: 'partials/clients/dialogs/clientfeature-edit.html',
                controller: 'EditClientFeatureController',
                resolve: {selectedClientFeature: function () {
                    return clientFeature;
                } }
            };
            //open dialog box
            $dialog.dialog(dialogOpts).open().then(function (result) {
                if (result) {
                    $scope.selectedClientFeature = result;
                    $scope.selectedClientFeature.$save({clientId: $scope.selectedClientFeature.client.id}, function (data, headers) {
                        console.log('saved.');
                    }, null);
                }
            });
        };
    });

我几乎完全不熟悉测试,并认为我可能需要测试两件事:

  1. 调用$ scope.editClientFeature()时会打开一个对话框
  2. 关闭对话框后成功调用$ save并返回'result'
  3. 我真正搞砸的测试现在看起来像这样:

    describe('ClientFeatureController', function () {
        var scope, $dialog, provider;
    
        beforeEach(function () {
                inject(function ($controller, $httpBackend, $rootScope, _$dialog_) {
                scope = $rootScope;
                $dialog = _$dialog_;
    
                //mock client
                scope.selected = {};
                scope.selected.client = {
                    id: 23805
                };
    
                $httpBackend.whenGET('http://localhost:3001/client/' + scope.selected.client.id + '/clientfeatures').respond(mockClientFeatures);
                $controller('ClientFeatureController', {$scope: scope});
                $httpBackend.flush();
            });
        });
    
    
        it('should inject dialog service from angular-ui-bootstrap module', function () {
            expect($dialog).toBeDefined();
            console.log($dialog); //{}
        });
    
        var dialog;
    
        var createDialog = function (opts) {
            dialog = $dialog.dialog(opts);
        };
    
        describe('when editing a clientfeature', function () {
            createDialog({});
            console.log(dialog); //undefined
    //        var res;
    //        var d;
    //        beforeEach(function () {
    //            var dialogOpts = {
    //                template: '<div>dummy template</div>'
    //            };
    //            console.log(dialog);
    //            d = $dialog.dialog(dialogOpts);
    //            d.open();
    //        });
    //
    //        it('should open a dialog when editing a client feature', function () {
    //            expect(d.isOpen()).toBe(true);
    //        });
        });
    
    });
    

    现在眼前的问题是我无法创建/打开对话框。我收到以下错误:

    Chrome 25.0 (Mac) ClientFeatureController when editing a clientfeature encountered a declaration exception FAILED
        TypeError: Cannot call method 'dialog' of undefined
    

    如果有人已经为类似的用例编写了一个测试并且可以为我提供一个例子,那就太好了,因为我很丢失。

    谢谢, 肖恩

4 个答案:

答案 0 :(得分:6)

我一直在努力解决同样的问题,直到现在,在使用github repo后,我发现对话框测试并将其用作起点:

var $dialog,$scope,$httpBackend;
  beforeEach(module('ui.bootstrap.dialog'));
  beforeEach(function(){
    inject(function (_$dialog_, _$httpBackend_, $controller){
      $dialog = _$dialog_;
      $httpBackend = _$httpBackend_;
      $httpBackend.expectGET('/appServer/list')
        .respond([{
            id:1,
            name:'test1'
          },
          {
            id:2,
            name:'test2'
          },
          {
            id:3,
            name:'test3'
          }]);


      //setup controller scope
      scope = {};
      ServerCtrl = $controller('ServerCtrl', {
        $scope: scope,
        $dialog:$dialog
      });
    });
  });

答案 1 :(得分:3)

我也更喜欢正确的模拟。当它不可用时,我修补服务

测试一下:

$dialog.messageBox(title, msg, btns)
   .open()
   .then(function (result) {
       if (result == 'ok') {
            // block executed if user click OK
       }
});

您可以像这样修补$ dialog:

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

答案 2 :(得分:2)

我个人试图嘲笑所有服务。如果ui-bootstrap项目没有提供$ dialog模拟,你应该在那里打开一个bug票并要求他们提供一个。然而创建一个就像这一样容易。

模拟服务应该有假方法,除了返回promise之外什么也不做。它还应该为您提供一种方法来刷新所有异步方法,以便更容易进行同步测试。

答案 3 :(得分:2)

我觉得写自己的对话模型最清楚。这是一个模拟对话框以模拟选择“是”的例子。

正在测试的代码

.controller('AdminListingCtrl', function AdminListingController($scope, $dialog, houseRepository) {
    $scope.houses = houseRepository.query();
    $scope.remove = function (house) {
        var dlg = $dialog.messageBox('Delete house', 'Are you sure?', [
            {label: 'Yep', result: 'yes'},
            {label: 'Nope', result: 'no'}
        ]);
        dlg.open().then(function (result) {
            if (result == 'yes') {
                houseRepository.remove(house.id);
                $scope.houses = houseRepository.query();
            }
        });
    };
}

测试

    describe('when deleting a house', function () {

        var fakeDialog = {
            open: function()
            {
                return {
                    then: function(callback) {
                        callback("yes");
                    }
                };
            }
        };

        beforeEach(inject(function($dialog) {
            spyOn($dialog, 'messageBox').andReturn(fakeDialog);
        }));

        it('should call the remove method on the houseRepository', function () {
            scope.remove({id: 99});
            expect(houseRepository.remove).toHaveBeenCalledWith(99);
        });

        // etc
    });