【问题标题】:Unit testing a modalInstance controller with Karma / Jasmine使用 Karma / Jasmine 对 modalInstance 控制器进行单元测试
【发布时间】:2014-03-07 09:55:17
【问题描述】:

编辑:本文结尾​​处的快速和肮脏的解决方案

我正在使用来自 AngularUI-Bootstrap 的模态窗口,其方式与网站上的解释相同,只是我拆分了文件。 因此我有:

CallingController.js:

$scope.delete = function () {
    if ($scope.selected.length > 0) {
        // [...]
        // preparing data
        // [...]
        var modalInstance = $modal.open({
            templateUrl: 'views/modalView.html',
            controller: 'modalCtrl',
            resolve: {
                itemArray: function () {
                    return $scope.selected;
                }
            }
        });
        modalInstance.result.then(function (confirm) {
            if (confirm === true) {
                // [...]
                // treat
                // [...]
            }
        });
    }
};

modalController.js:

myAppControllers.controller('modalCtrl',
    function ($scope, $modalInstance, itemArray) {

        $scope.accept = function () {
            $modalInstance.close(true);
        };

        $scope.reject = function () {
            $modalInstance.close(false);
        };

        $scope.itemArray = itemArray;

    });

当我使用 Karma 测试此代码时(在 karma 配置文件中加载了 ui-bootstrap-tpls.min.js 文件),我收到以下错误: 错误:[$injector:unpr] [http://errors.angularjs.org/1.2.15-build.2389+sha.c5f2f58/$injector/unpr?p0=%24modalInstanceProvider%20%3C-%20%24modalInstance]1 at Error (native),意思是茉莉无法找到 $modalInstance 的提供者。

我什至没有在这个控制器上测试东西,还没有,但这是我的 jasmine 测试文件:

testModalController.js:

describe('Controller: modalCtrl', function () {

    beforeEach(module('myApp'));

    var Ctrl;
    var scope;

    // Initialize the controller and a mock scope
    beforeEach(inject(
        function ($controller, $rootScope) {
            scope = $rootScope.$new();

            Ctrl = $controller('modalCtrl', { $scope: scope });
        })
    );

    describe('Initial state', function () {
        it('should instantiate the controller properly', function () {
            expect(Ctrl).not.toBeUndefined();
        });

        it('should initialize its values properly', function () {

        });
    });

});

你对这个问题有任何线索吗?这不是我使用(和测试)的第一个“外部”模块,我做了与其他模块相同的事情,只是这次它不起作用,我不知道为什么。

===========================================强>

编辑:快速且可能很脏的解决方案:

好的,所以基于 Jasmine 控制器实例化中的作用域模拟方法,我想出了如何“解决”我的问题,但它可能很脏,所以如果你找到更好的方法,请随时发表评论我的意图。

testModalController.js:

describe('Controller: modalCtrl', function () {

    beforeEach(module('myApp'));

    var Ctrl;
    var scope;
    var modalInstance;

    // Initialize the controller and a mock scope
    beforeEach(inject(
        function ($controller, $rootScope, _$modal_) {
            scope = $rootScope.$new();
            modalInstance = _$modal_.open({
                templateUrl: 'views/modalView.html'
            });

            Ctrl = $controller('modalCtrl', {
                $scope: scope,
                $modalInstance: modalInstance,
                itemArray: function () { return ['a', 'b', 'c']; }
            });
        })
    );

    describe('Initial state', function () {
        it('should instantiate the controller properly', function () {
            expect(Ctrl).not.toBeUndefined();
        });

        it('should initialize its values properly', function () {

        });
    });

});

这样,Jasmine 不再搜索提供程序,因为您已经注入了应该需要这些提供程序的项目。它有效,但我相信它可以以更好的方式完成......

【问题讨论】:

  • 你需要加载引导模块('ui.bootstrap')或者可能('ui.bootstrap.modal')。试着告诉我。还有一个笨拙的人总是有帮助。
  • 同样的事情。添加了依赖项,但在控制台窗口中不断弹出相同的错误。有什么解决办法吗?是什么触发了它?我试过here,它工作正常,但在我的应用程序向左一步,它在控制台中发送这个地狱般的错误。

标签: angularjs jasmine karma-runner angular-ui-bootstrap karma-jasmine


【解决方案1】:

我通过创建模拟 modalmodalInstance 对象并验证它们是否已被我的控制器代码调用来解决这个问题。由于 modalmodalInstance 是第三方库的一部分,因此我们没有责任测试它们是否正常工作 - 相反,我们有责任测试调用该库的代码是否正常工作。

使用您的示例:

describe('Controller: modalCtrl', function () {

  beforeEach(module('myApp'));

  var Ctrl;
  var scope;
  var modalInstance;

  // Initialize the controller and a mock scope
  beforeEach(inject(
    function ($controller, $rootScope) {     // Don't bother injecting a 'real' modal
      scope = $rootScope.$new();
      modalInstance = {                    // Create a mock object using spies
        close: jasmine.createSpy('modalInstance.close'),
        dismiss: jasmine.createSpy('modalInstance.dismiss'),
        result: {
          then: jasmine.createSpy('modalInstance.result.then')
        }
      };
      Ctrl = $controller('modalCtrl', {
        $scope: scope,
        $modalInstance: modalInstance,
        itemArray: function () { return ['a', 'b', 'c']; }
      });
    })
  );

  describe('Initial state', function () {
    it('should instantiate the controller properly', function () {
      expect(Ctrl).not.toBeUndefined();
    });

    it('should close the modal with result "true" when accepted', function () {
      scope.accept();
      expect(modalInstance.close).toHaveBeenCalledWith(true);
    });

    it('should close the modal with result "false" when rejected', function () {
      scope.reject();
      expect(modalInstance.close).toHaveBeenCalledWith(false);
    });
  });
});

这样,我们真的不需要对 Angular-UI 对象有任何依赖,而且我们的单元测试很好且独立。

【讨论】:

  • 感谢您的回答,我喜欢它。我一直在努力尝试以一种好的方式测试我的模态,我认为这处理得很好!
  • 这是正确的。重要的是不要期望 $modalInstance 在一个人的测试中对注入器可用,因为它会期望 uiBootstrap 模块的其他服务,因为 $modalInstance 是使用 $modal 服务的新模式的实例化(哪个可用于注射器)。
【解决方案2】:

代替:

modalInstance = {                    // Create a mock object using spies
  close: jasmine.createSpy('modalInstance.close'),
  dismiss: jasmine.createSpy('modalInstance.dismiss'),
  result: {
    then: jasmine.createSpy('modalInstance.result.then')
  }
};

这可以写成:

modalInstance = jasmine.createSpyObj('modalInstance', ['close', 'dismiss', 'result.then']);

也没有 $modalInstance 现在是 $uibModalInstance 所以上面的每个“modalInstance”都应该替换为“uibModalInstance”

【讨论】:

  • +1 获取有关 $uibModalInstance 的更新。显然,这在 2015 年 10 月中旬左右发生了变化,当时他们从 0.14.3 迁移到 1.0.0。
【解决方案3】:

+1 fiznool 的回答。它是正确的,应该选择..

不过,我想指出一件事,它在此处呈现的方式是不可维护的。

由于这是有棱角的,我建议使用它..

angular.module('...').service('$modalInstance', function(){
   ... define spies and such 
})

将使您的代码更加模块化和通用。 只需在spec 下添加一个包含上述内容的文件,并确保将其包含在您的karma.conf

如果您想确保它仅在特定测试中加载,只需给它一个唯一的模块名称并将其添加到 beforeEach 中的 module 调用

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-24
    • 2014-08-07
    • 1970-01-01
    相关资源
    最近更新 更多