Тестирование модульного контроллера экземпляра AngularUI Bootstrap
Это несколько следующий вопрос к этому: Mocking $modal в модульных тестах AngularJS
Ссылаемый SO - отличный вопрос с очень полезным ответом. Вопрос, который у меня остался после этого, заключается в следующем: как мне unit test модальный контроллер экземпляра? В SO, на который ссылается, проверяющий контроллер тестируется, но модальный контроллер экземпляра высмеивается. Вероятно, последний также должен быть протестирован, но это оказалось очень сложным. Вот почему:
Я скопирую тот же пример из ссылки SO здесь:
.controller('ModalInstanceCtrl', function($scope, $modalInstance, items){
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
Итак, моя первая мысль заключалась в том, что я просто создаю экземпляр контроллера непосредственно в тесте, как и любой другой тестируемый контроллер:
beforeEach(inject(function($rootScope) {
scope = $rootScope.$new();
ctrl = $controller('ModalInstanceCtrl', {$scope: scope});
});
Это не работает, потому что в этом контексте angular не имеет провайдера для ввода $modalInstance, поскольку он предоставляется модальным интерфейсом.
Затем я перехожу к плану B: используйте $modal.open для создания экземпляра контроллера. Это будет работать как ожидалось:
beforeEach(inject(function($rootScope, $modal) {
scope = $rootScope.$new();
modalInstance = $modal.open({
template: '<html></html>',
controller: 'ModalInstanceCtrl',
scope: scope
});
});
(Обратите внимание, что шаблон не может быть пустой строкой, иначе он будет ошибочным.)
Теперь проблема заключается в том, что я не вижу видимости в области, которая является обычным способом сбора ресурсов unit test и т.д. В моем реальном коде контроллер вызывает службу ресурсов для заполнения списка вариантов; моя попытка проверить это устанавливает ожидать, что GET удовлетворяет службе, которую использует мой контроллер, и я хочу проверить, что контроллер помещает результат в свою область. Но модальный создает новую область для модального контроллера экземпляра (используя область действия, которую я передаю в качестве прототипа), и я не могу понять, как я могу получить отверстие этой области. Объект modalInstance не имеет окна в контроллере.
Любые предложения по "правильному" способу проверить это?
(N.B.: поведение создания области производных для модального контроллера экземпляра не является неожиданным - это документированное поведение. Мой вопрос о том, как его проверить, по-прежнему действителен независимо.)
Ответы
Ответ 1
Я тестирую контроллеры, используемые в модальных диалогах, напрямую создавая экземпляр контроллера (так же, как вы изначально предполагали сделать это выше).
Так как там нет издевательства версии $modalInstance
, я просто создаю mock-объект и передаю его в контроллер.
var modalInstance = { close: function() {}, dismiss: function() {} };
var items = []; // whatever...
beforeEach(inject(function($rootScope) {
scope = $rootScope.$new();
ctrl = $controller('ModalInstanceCtrl', {
$scope: scope,
$modalInstance: modalInstance,
items: items
});
}));
Теперь зависимости для контроллера выполнены, и вы можете протестировать этот контроллер, как и любой другой контроллер.
Например, я могу сделать spyOn(modalInstance, 'close')
, а затем утверждать, что мой контроллер закрывает диалог в соответствующее время.
Ответ 2
В качестве альтернативы, если вы используете жасмин, вы можете издеваться над $uibModalInstance
с помощью метода createSpy
:
beforeEach(inject(function ($controller, $rootScope) {
$scope = $rootScope.$new();
$uibModalInstance = jasmine.createSpyObj('$uibModalInstance', ['close', 'dismiss']);
ModalCtrl = $controller('ModalCtrl', {
$scope: $scope,
$uibModalInstance: $uibModalInstance,
});
}));
И протестируйте его, не называя spyOn
для каждого метода, скажем, у вас есть 2 метода scope, cancel()
и confirm()
:
it('should let the user dismiss the modal', function () {
expect($scope.cancel).toBeDefined();
$scope.cancel();
expect($uibModalInstance.dismiss).toHaveBeenCalled();
});
it('should let the user confirm the modal', function () {
expect($scope.confirm).toBeDefined();
$scope.confirm();
expect($uibModalInstance.close).toHaveBeenCalled();
});
Ответ 3
То же самое происходит с $uidModalInstance, и вы можете решить его аналогичным образом:
var uidModalInstance = { close: function() {}, dismiss: function() {} };
$ctrl = $controller('ModalInstanceCtrl', {
$scope: $scope,
$uibModalInstance: uidModalInstance
});
или, как сказал @yvesmancera, вы можете использовать метод jasmine.createSpy, например:
var uidModalInstance = jasmine.createSpyObj('$uibModalInstance', ['close', 'dismiss']);
$ctrl = $controller('ModalInstanceCtrl', {
$scope: $scope,
$uibModalInstance: uidModalInstance
});
Ответ 4
Следуйте приведенным ниже шагам:
-
Определите заглушку для ModalInstance, как показано ниже
uibModalInstanceStub = {
close: sinon.stub(),
dismiss: sinon.stub()
};
-
Передайте заглушку модального экземпляра при создании контроллера
function createController() {
return $controller(
ppcConfirmGapModalComponentFullName,
{
$scope: scopeStub,
$uibModalInstance: uibModalInstanceStub
});
}
});
-
Методы Stub close(), reject() будет вызван как часть тестов
it ('confirm modal - подтвердить действие подтверждения, по вызову ok() modalInstance close() function', function() { action = 'Ok'; scopeStub.item = testItem; createController(); scopeStub.ok();
});