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

angular-ui/bootstrap: Testing a controller that uses a dialog

我有一个控制器,它使用来自 angular-ui/bootstrap 的对话框:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
   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);
                }
            });
        };
    });

我对测试几乎完全陌生,我想也许我需要测试两件事:

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    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: 'dummy template'
    //            };
    //            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);
    //        });
        });

    });

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

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

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

    谢谢,
    肖恩


    直到现在,我一直在为同样的问题苦苦挣扎,在浏览了 github 存储库之后,我找到了对话测试并将其用作起点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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
    2
    3
    4
    5
    6
    7
    $dialog.messageBox(title, msg, btns)
       .open()
       .then(function (result) {
           if (result == 'ok') {
                // block executed if user click OK
           }
    });

    你可以像这样给 $dialog 打补丁:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $dialog.messageBox = function (title, msg, btns) {
        return {
            open: function () {
                return {
                     then: function (callback) {
                          callback('ok'); // 'ok' will be set to param result
                     }
                 }
            }
        }
     };

    我发现编写我自己的对话框模拟是最清晰的。这是一个模拟对话框以模拟选择"是"的示例。

    被测代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    .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();
                }
            });
        };
    }

    测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
        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
        });

    我个人尝试模拟所有服务。如果 ui-bootstrap 项目不提供 $dialog 模拟,您应该在那里打开一个错误票并要求他们提供一个。但是创建一个同样容易。

    模拟服务应该有虚假的方法,除了返回Promise之外什么都不做。它还应该为您提供一种刷新所有异步方法的方法,以便更轻松地进行同步测试。