Модульное тестирование модального контроллера экземпляра с помощью Karma / Jasmine

РЕДАКТИРОВАТЬ: быстрое и грязное решение в конце этого сообщения

Я использую модальное окно из 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 (с файлом ui-bootstrap-tpls.min.js, загруженным в файл конфигурации karma), я получаю следующую ошибку: Ошибка: [$ injector : unpr] [http://errors.angularjs.org/1.2.15-build.2389+sha.c5f2f58/ $ injector / unpr? p0 =% 24modalInstanceProvider% 20% 3C-% 20% 24modalInstance] 1 с ошибкой (родной), что означает, что жасмин не может найти поставщик для $ modalInstance.

Я даже не тестирую что-то на этом контроллере, пока нет, но вот мой тестовый файл жасмина:

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 больше не ищет поставщиков, потому что вы уже ввели элементы, которые, как предполагается, нуждаются в этих поставщиках. Это работает, но я считаю, что это можно было бы сделать лучше ...


person Manyuuz    schedule 07.03.2014    source источник
comment
Вам нужно загрузить модуль начальной загрузки ('ui.bootstrap') или, возможно, ('ui.bootstrap.modal'). Попробуй и скажи мне. Также всегда поможет плункер.   -  person Jesus Rodriguez    schedule 07.03.2014
comment
То же самое. Добавлены зависимости, но та же ошибка продолжает появляться в окне консоли. Любое решение? Что вызывает это? Я попробовал здесь, и он работает нормально, но в моем приложении шаг влево и он отправляет эту адскую ошибку в консоль.   -  person Eugene    schedule 07.04.2014


Ответы (3)


Я решаю эту проблему, просто создавая фиктивные объекты modal и modalInstance и проверяя, что они были вызваны кодом моего контроллера. Поскольку modal и modalInstance являются частью сторонней библиотеки, мы не несем ответственности за проверку правильности их работы - скорее, мы обязаны проверить, что наш код, вызывающий библиотеку, работает нормально.

Используя ваш пример:

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, а наши модульные тесты красивы и изолированы.

person Tom Spencer    schedule 16.04.2014
comment
Спасибо за этот ответ, мне это нравится. Некоторое время я боролся, пытаясь хорошо протестировать свои модальные окна, и я думаю, что это хорошо с этим справляется! - person Mik Cox; 13.05.2014
comment
Это правильно. Важно добавить, чтобы не ожидать, что $ modalInstance будет доступен инжектору в своих тестах, поскольку этого можно было бы ожидать от других служб модуля uiBootstrap, потому что $ modalInstance - это создание нового модального окна с использованием $ modal service ( который доступен для инжектора). - person Morris Singer; 11.09.2014

Вместо того:

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"

person Jason Rice    schedule 11.04.2016
comment
+1 за обновление о $ uibModalInstance. Очевидно, это изменилось примерно в середине октября 2015 года, когда они перешли с 0,14,3 на 1,0,0. - person Scott Fraley; 09.09.2016

+1 за физноол ответ. это правильно и нужно выбирать ..

Однако я хотел бы отметить одну вещь, она не обслуживается в том виде, в котором она представлена ​​здесь.

Так как это угловой, я предлагаю использовать его ..

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

сделает ваш код более модульным и универсальным. просто добавьте в папку spec файл с указанным выше содержанием и обязательно включите его в свой karma.conf

если вы хотите, чтобы он загружался только в определенных тестах, просто дайте ему уникальное имя модуля и добавьте его в module вызов в beforeEach

person guy mograbi    schedule 25.09.2015