컨트롤러를 전역으로 만들지 않고 Angular의 단위 테스트 지시어 컨트롤러
Vojta Jina의 우수한 저장소에서 지시문 테스트를 시연하고 모듈 래퍼 외부에 지시어 컨트롤러를 정의합니다. 여기를 참조하십시오 : https://github.com/vojtajina/ng-directive-testing/blob/master/js/tabs.js
나쁜 습관이 아니고 글로벌 네임 스페이스를 오염시키지 않습니까?
TabsController를 호출하는 것이 합리적 일 수있는 다른 장소가 있다면 그게 문제가되지 않을까요?
언급 된 지침에 대한 테스트는 https://github.com/vojtajina/ng-directive-testing/commit/test-controller 에서 찾을 수 있습니다.
컨트롤러를 전역 네임 스페이스에 배치하지 않고 지시문 컨트롤러를 나머지 지시문과 별도로 테스트 할 수 있습니까?
app.directive (...) 정의 내에서 전체 지시문을 캡슐화하는 것이 좋습니다.
훌륭한 질문입니다!
따라서 이것은 컨트롤러뿐만 아니라 지시문이 작업을 수행하는 데 필요할 수있는 서비스에 대해서도 잠재적으로이 컨트롤러 / 서비스를 "외부 세계"에 노출시키고 싶지는 않은 일반적인 문제입니다.
나는 글로벌 데이터가 악하고 피해야 한다고 굳게 믿으며 이것은 지시 통제자들에게도 적용됩니다 . 이 가정을한다면 컨트롤러를 "로컬"로 정의하기 위해 여러 가지 접근 방식을 취할 수 있습니다. 그렇게하는 동안 컨트롤러는 유닛 테스트에 "쉽게"액세스 할 수 있어야 하므로 단순히 지시문의 클로저에 숨길 수는 없습니다. IMO 가능성은 다음과 같습니다.
1) 첫째, 모듈 수준에서 지시어의 컨트롤러를 간단히 정의 할 수 있습니다 . 예 :
angular.module('ui.bootstrap.tabs', [])
.controller('TabsController', ['$scope', '$element', function($scope, $element) {
...
}])
.directive('tabs', function() {
return {
restrict: 'EA',
transclude: true,
scope: {},
controller: 'TabsController',
templateUrl: 'template/tabs/tabs.html',
replace: true
};
})
이것은 Vojta의 작업을 기반으로하는 https://github.com/angular-ui/bootstrap/blob/master/src/tabs/tabs.js 에서 사용하는 간단한 기술입니다 .
이것은 매우 간단한 기술이지만 컨트롤러는 여전히 전체 응용 프로그램에 노출되어 있으므로 다른 모듈이 잠재적으로이를 재정의 할 수 있습니다. 이런 의미에서 컨트롤러를 AngularJS 응용 프로그램에 로컬로 만들지 만 (전역 창 범위를 오염시키지 않음) 모든 AngularJS 모듈에 대해 전역 적으로 만듭니다.
2) 테스트를 위해 클로저 범위와 특수 파일 설정을 사용합니다 .
컨트롤러 함수를 완전히 숨기려면 코드를 클로저로 감쌀 수 있습니다. 이것은 AngularJS가 사용하는 기술입니다. 예를 들어 NgModelController를 살펴보면 자체 파일에서 "전역"함수로 정의되어 있으므로 테스트를 위해 쉽게 액세스 할 수 있지만 전체 파일은 빌드 시간 동안 닫혀 있습니다.
- https://github.com/angular/angular.js/blob/master/src/angular.prefix
- https://github.com/angular/angular.js/blob/master/src/angular.suffix
요약하자면 옵션 (2)는 "안전"하지만 빌드를 위해 약간의 사전 설정이 필요합니다.
때때로 지시문과 함께 컨트롤러를 포함하는 것을 선호하므로이를 테스트 할 방법이 필요합니다.
먼저 지침
angular.module('myApp', [])
.directive('myDirective', function() {
return {
restrict: 'EA',
scope: {},
controller: function ($scope) {
$scope.isInitialized = true
},
template: '<div>{{isInitialized}}</div>'
}
})
그런 다음 테스트 :
describe("myDirective", function() {
var el, scope, controller;
beforeEach inject(function($compile, $rootScope) {
# Instantiate directive.
# gotacha: Controller and link functions will execute.
el = angular.element("<my-directive></my-directive>")
$compile(el)($rootScope.$new())
$rootScope.$digest()
# Grab controller instance
controller = el.controller("myDirective")
# Grab scope. Depends on type of scope.
# See angular.element documentation.
scope = el.isolateScope() || el.scope()
})
it("should do something to the scope", function() {
expect(scope.isInitialized).toBeDefined()
})
})
See angular.element documentation for more ways to get data from an instantiated directive.
Beware that instantiating the directive implies that the controller and all link functions will already have run, so that might affect your tests.
James's method works for me. One small twist is though, when you have an external template, you would have to call $httpBackend.flush() before $rootScope.$digest() in order to let angular execute your controller.
I guess this should not be an issue, if you are using https://github.com/karma-runner/karma-ng-html2js-preprocessor
Is there something wrong with doing it this way? Seems preferable since you avoid placing your controller in the global name space and are able to test what you want (i.e. the controller) without unnecessarily $compiling html.
Example directive definition:
.directive('tabs', function() {
return {
restrict: 'EA',
transclude: true,
scope: {},
controller: function($scope, $attrs) {
this.someExposedMethod = function() {};
},
templateUrl: 'template/tabs/tabs.html',
replace: true
};
Then in your Jasmine test, ask for the directive you created using "name + Directive" (ex. "tabsDirective"):
var tabsDirective = $injector.get('tabsDirective')[0];
// instantiate and override locals with mocked test data
var tabsDirectiveController = $injector.instantiate(tabsDirective.controller, {
$scope: {...}
$attrs: {...}
});
Now you can test controller methods:
expect(typeof tabsDirectiveController.someExposedMethod).toBe('function');
Use IIFE, which is a common technique to avoid global namespace conflict & it also save tricky inline gymnastics, plus provide freedom in your scope.
(function(){
angular.module('app').directive('myDirective', function(){
return {
.............
controller : MyDirectiveController,
.............
}
});
MyDirectiveController.$inject = ['$scope'];
function MyDirectiveController ($scope) {
}
})();
'programing tip' 카테고리의 다른 글
Python Pandas groupby 작업 결과를 부모 데이터 프레임의 열에 다시 할당하는 방법은 무엇입니까? (0) | 2020.11.14 |
---|---|
SVN은 커밋 된 코드에서 패치를 생성합니까? (0) | 2020.11.14 |
사용자 정의 http 헤더를 어떻게 추가합니까? (0) | 2020.11.14 |
Groovy-객체를 JSON 문자열로 변환 (0) | 2020.11.14 |
C0103 메시지를 중지하도록 PyLint에 "상수가 아닌 변수입니다"라고 어떻게 말합니까? (0) | 2020.11.14 |