source

컨트롤러를 글로벌하게 하지 않고 Angular에서 유닛 테스트 지시 컨트롤러

ittop 2023. 3. 18. 09:22
반응형

컨트롤러를 글로벌하게 하지 않고 Angular에서 유닛 테스트 지시 컨트롤러

Vojta Jina의 뛰어난 저장소에서 디렉티브 컨트롤러를 모듈 래퍼 외부에 정의합니다.여기를 참조해 주세요.https://github.com/vojtajina/ng-directive-testing/blob/master/js/tabs.js

그것은 나쁜 관행이고 세계적인 네임스페이스를 오염시키지 않나요?

어떤 것을 Tabs Controller라고 부르는 것이 논리적인 다른 장소를 갖게 되면, 그 장소가 망가지는 것은 아닐까요?

전술한 디렉티브에 대한 테스트는 https://github.com/vojtajina/ng-directive-testing/commit/test-controller 에서 확인할 수 있습니다.

글로벌 네임스페이스에 컨트롤러를 배치하지 않고 디렉티브컨트롤러를 다른 디렉티브컨트롤러와 분리하여 테스트할 수 있습니까?

전체 디렉티브를 app.directive(...) 정의로 캡슐화하는 것이 좋습니다.

나는 가끔 명령어와 함께 내 컨트롤러를 포함하는 것을 선호하기 때문에 그것을 테스트할 방법이 필요하다.

우선 지시사항

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()
  })
})

인스턴스화된 지시문에서 데이터를 가져오는 자세한 방법은 angular.element 설명서를 참조하십시오.

디렉티브 인스턴스화는 컨트롤러 및 모든 링크 기능이 이미 실행되었음을 의미하므로 테스트에 영향을 줄 수 있습니다.

훌륭한 질문입니다!

따라서 이는 컨트롤러뿐만 아니라 지시가 그 작업을 수행하기 위해 필요할 수 있는 서비스에서도 공통적으로 우려되는 사항이지만, 반드시 이 컨트롤러/서비스를 "외부"에 공개하고 싶지는 않습니다.

글로벌 데이터는 악성이며 피해야 하며 이는 디렉티브 컨트롤러에도 적용됩니다.이 전제를 도입하면, 이러한 컨트롤러를 「로컬」로 정의하기 위해서, 몇개의 다른 어프로치를 취할 수 있습니다. 동안 컨트롤러는 유닛 테스트에 "간단하게" 접근할 수 있어야 하므로 단순히 명령어 폐쇄에 숨길 수 없습니다.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에서 사용하고 있는 간단한 기술입니다.

이것은 매우 간단한 기술이지만 컨트롤러는 여전히 애플리케이션 전체에 노출되어 있기 때문에 다른 모듈이 이를 덮어쓸 수 있습니다.이러한 의미에서 컨트롤러를 Angular에 로컬로 만듭니다.JS 응용 프로그램(글로벌 창 범위를 오염시키지 않음)뿐만 아니라 모든 Angular에 글로벌하게 적용됩니다.JS 모듈

2) Closure Scope와 특수 파일 셋업을 사용하여 테스트한다.

컨트롤러 기능을 완전히 숨기려면 코드를 폐쇄형으로 감쌀 수 있습니다.이 기술은 Angular가JS가 사용하고 있다.예를 들어 NgModelController를 보면 자체 파일에서 "글로벌" 함수로 정의되어 있으며(따라서 테스트를 위해 쉽게 액세스할 수 있음), 빌드 시간 동안 파일 전체가 닫힙니다.

정리하면, 옵션 (2)는 「안전」하지만, 빌드에는 사전에 설정이 필요합니다.

제임스의 방법은 나에게 효과가 있다.단, 외부 템플릿이 있는 경우 $httpBackend를 호출해야 합니다.$rootScope 앞에 flush()를 입력합니다.각도가 컨트롤러를 실행할 수 있도록 하기 위해 $squal()을 지정합니다.

https://github.com/karma-runner/karma-ng-html2js-preprocessor 를 사용하고 있는 경우는, 이것은 문제가 되지 않는다고 생각합니다.

이런 식으로 하는 게 뭐가 잘못됐나요?컨트롤러를 글로벌 네임스페이스에 배치하지 않고 불필요한 $compiling html 없이 원하는 것(컨트롤러 등)을 테스트할 수 있으므로 권장됩니다.

지시 정의 예:

 .directive('tabs', function() {
  return {
    restrict: 'EA',
    transclude: true,
    scope: {},
    controller: function($scope, $attrs) {
      this.someExposedMethod = function() {};
    },
    templateUrl: 'template/tabs/tabs.html',
    replace: true
  };

그런 다음 Jasmine 테스트에서 "name + Directive"(예: "tabs Directive")를 사용하여 작성한 디렉티브를 묻습니다.

var tabsDirective = $injector.get('tabsDirective')[0];
// instantiate and override locals with mocked test data
var tabsDirectiveController = $injector.instantiate(tabsDirective.controller, {
  $scope: {...}
  $attrs: {...}
});

이제 컨트롤러 방식을 테스트할 수 있습니다.

expect(typeof tabsDirectiveController.someExposedMethod).toBe('function');

IIFE를 사용하면 글로벌 네임스페이스 경합을 회피할 수 있습니다.또, 복잡한 인라인 체조를 회피할 수 있어 스코프의 자유도 얻을 수 있습니다.

 (function(){

  angular.module('app').directive('myDirective', function(){
     return {
       .............
       controller : MyDirectiveController,
       .............
     }
  });

  MyDirectiveController.$inject = ['$scope'];

  function MyDirectiveController ($scope) {

  }

})();

언급URL : https://stackoverflow.com/questions/15314293/unit-testing-directive-controllers-in-angular-without-making-controller-global

반응형