Модульное тестирование контроллеров с формами в AngularJS
У меня следующая ситуация с Angular:
Controller
function Controller () {
// form used in template
this.form ...
}
Шаблон, который имеет форму и использует этот контроллер
Template
<div ng-controller="Controller as ctrl">
<form name="ctrl.form">
...
</div>
Я должен сказать, что я в общем смущен, почему Angular не имеет лучшего способа добавления формы в контроллер, кроме автоматического добавления this.form
или же $scope.form
В зависимости от того, как вы используете контроллеры, но это, я думаю, другой вопрос.
Реальная проблема, с которой я столкнулся сейчас, заключается в том, что я не уверен, как мне это проверить. Если я просто создаю экземпляр контроллера в тесте, то моя форма не определена
$controller('Controller as ctrl')
Я нашел способ, просто скомпилировав шаблон
$scope = $rootScope.$new();
$compile(template)($scope);
Но потому что ng-controller
в шаблоне начинается новая область, я не могу получить доступ к контроллеру напрямую с $scope.ctrl
вместо этого я должен был бы сделать что-то вроде $scope.$$childHead.login
... и я чувствую, что это становится слишком сложно. edit: не говоря уже о том, что $$ указывает на частное свойство.
2 ответа
Я решил это сам, но я оставляю это здесь неприемлемым, потому что я не думаю, что решение очень хорошее. Если кто-нибудь знает лучший способ, пожалуйста, напишите.
Проблема с компиляцией шаблонов заключается в том, что также невозможно вставить фиктивные сервисы в контроллер, по крайней мере, я не понял этого. Вы получаете экземпляр контроллера в $scope, например $scope.ctrl
, но это все.
Вторая попытка состояла в том, чтобы найти только форму в шаблоне, скомпилировать и добавить ее в контроллер, который был создан отдельно. Это сработало, но не совсем, потому что область действия $ для формы и контроллера была разной, и поэтому любое обновление поля не отражало состояние контроллера.
В конце концов, он работает, как создать экземпляр контроллера с $ scope
ctrl = $controller('Controller as ctrl', {
someDependency: mockDependency,
$scope: $scope
});
а затем скомпилировать частичный шаблон (только форму) с той же $ scope
var formTpl = angular.element(template).find('form');
form = $compile(formTpl)($scope);
Таким образом, контроллер заканчивается в $scope.ctrl
, но потому что форма уже названа name="ctrl.form"
он вставляется в $scope.ctrl.form
и это видно внутри контроллера.
Когда используешь controllerAs
Вы можете получить доступ к своей форме в тестах следующим образом:
// 1. Create a new scope
var $scope = $rootScope.$new();
// 2. Run the controller
var Controller = $controller("Controller as ctrl", { $scope: $scope });
// 3. Compile the template against our scope
// This will add property $scope.ctrl.form (assuming that form has name="ctrl.form")
$compile(angular.element(templateHtml))($scope);
// 4. Controller.form should now also be defined
expect(Controller.form).toBeDefined();
Между тем, насмешка может быть достигнута с помощью angular в $ обеспечить:
beforeEach(angular.mock.module(function ($provide) {
function ApiServiceMock() {
this.getName() = function () {
return "Fake Name";
};
}
// Provide our mocked service instead of 'ApiService'
// when our controller's code requests to inject it
$provide.value("ApiService", ApiServiceMock);
}));
Полный пример (с использованием обоих: макет и компиляция форм с controllerAs):
Основной код приложения:
angular.module("my.module", [])
.service("ApiService", function () {
this.getName = function () {
return "Real Name";
};
})
.controller("Controller", function (ApiService) {
var ctrl = this;
ctrl.someProperty = ApiService.getName();
});
HTML:
<div ng-controller="Controller as ctrl">
<form name="ctrl.form">
<input type="email" name="email" />
</form>
</div>
Тестовое задание:
describe("Controller", function () {
var $scope,
Controller;
beforeEach(angular.mock.module("my.module"));
beforeEach(angular.mock.module(function ($provide) {
function ApiServiceMock() {
this.getName() = function () {
return "Fake Name";
};
}
$provide.value("ApiService", ApiServiceMock);
}));
beforeEach(inject(function ($rootScope, $controller, $compile) {
$scope = $rootScope.$new();
Controller = $controller("Controller as ctrl", { $scope: $scope });
// FIXME: Use your own method of retrieving template's HTML instead 'getTemplateContents' placeholder
var html = getTemplateContents("./my.template.html"),
formTpl = angular.element(html).find('form');
$compile(formTpl)($scope);
}));
it('should contain someProperty', function () {
expect(Controller.someProperty).toBeDefined();
});
it('form should contain an email field', function () {
expect(Controller.form.email).toBeDefined();
});
});