AngularJS에서 격리 된 범위 지시문을 단위 테스트하는 방법


81

AngularJS에서 격리 된 범위를 단위 테스트하는 좋은 방법은 무엇입니까?

단위 테스트를 보여주는 JSFiddle

지시어 스 니펫

    scope: {name: '=myGreet'},
    link: function (scope, element, attrs) {
        //show the initial state
        greet(element, scope[attrs.myGreet]);

        //listen for changes in the model
        scope.$watch(attrs.myGreet, function (name) {
            greet(element, name);
        });
    }

지시문이 변경 사항을 수신하고 있는지 확인하고 싶습니다 . 격리 된 범위 에서는 작동 하지 않습니다 .

    it('should watch for changes in the model', function () {
        var elm;
        //arrange
        spyOn(scope, '$watch');
        //act
        elm = compile(validHTML)(scope);
        //assert
        expect(scope.$watch.callCount).toBe(1);
        expect(scope.$watch).toHaveBeenCalledWith('name', jasmine.any(Function));
    });

업데이트 : 예상되는 감시자가 자식 범위에 추가되었는지 확인하여 작동하도록했지만 매우 취약하며 아마도 문서화되지 않은 방식으로 접근자를 사용합니다 (예고없이 변경 될 수 있음!).

//this is super brittle, is there a better way!?
elm = compile(validHTML)(scope);
expect(elm.scope().$$watchers[0].exp).toBe('name');

업데이트 2 : 내가 언급했듯이 이것은 부서지기 쉽습니다! 아이디어는하지만 여전히 접근이 변경되었습니다 AngularJS와 새로운 버전의 작동 scope()isolateScope():

//this is STILL super brittle, is there a better way!?
elm = compile(validHTML)(scope);                       
expect(elm.isolateScope().$$watchers[0].exp).toBe('name');

스파이를 설정하는 방법을 찾았습니까?
tusharmath 2014

@Tushar는 실제로 작동하도록하는 방법이 있지만 예고없이 변경 될 수 있으므로 자신의 책임하에 사용하십시오.
daniellmb 2014

답변:


102

각도 요소 API 문서를 참조하십시오 . element.scope () 를 사용 하면 지시문의 범위 속성에 정의한 요소의 범위를 가져옵니다. element.isolateScope () 를 사용 하면 전체 격리 된 범위를 얻습니다. 예를 들어, 지시문이 다음과 같은 경우 :

scope : {
 myScopeThingy : '='
},
controller : function($scope){
 $scope.myIsolatedThingy = 'some value';
}

그런 다음 테스트에서 element.scope ()를 호출하면

{ myScopeThingy : 'whatever value this is bound to' }

그러나 element.isolateScope ()를 호출하면

{ 
  myScopeThingy : 'whatever value this is bound to', 
  myIsolatedThingy : 'some value'
}

이것은 각도 1.2.2 또는 1.2.3에서 사실이며 정확히 확실하지 않습니다. 이전 버전에서는 element.scope () 만있었습니다.


1
v1.2.3 피트 (jqLite) 범위에 게터 유사한 isolateScope () 노출 github.com/angular/angular.js/commit/...
daniellmb

1
하지만 $ watch 방법을 어디에서 감시합니까?
tusharmath 2014

1
$ watch에서 실행되는 함수를 노출 한 다음 감시 할 수 있습니다. 지시문에서 "scope.myfunc = function () ..."을 설정 한 다음 $ watch에서 "$ scope. $ watch ( 'myName', scope.myfunc);"를 수행하십시오. 이제 테스트에서 격리 된 범위에서 myFunc를 가져 와서 감시 할 수 있습니다.
Yair Tavor 2014

22
나를 위해 작동하지 않습니다. element.isolateScope()를 반환합니다 undefined. 그리고 element.scope()내 범위에 넣은 모든 항목을 포함하지 않는 범위를 반환합니다.
mcv

4
@mcv 내가 할 필요가 발견element.children().isolateScope()
윌 킬 링

11

var isolateScope = myDirectiveElement.scope()격리 범위를 얻으려면 할 수 있습니다 .

하지만 $ watch가 호출되었는지 테스트 할 필요는 없습니다. 앱을 테스트하는 것보다 angularjs를 테스트하는 것이 더 많습니다. 하지만 질문에 대한 예일 뿐이라고 생각합니다.


2
나는 그것이 "각도를 테스트하는 것"이라는 것에 동의하는지 확신하지 못한다. 나는 $ watch가 작동하는지 테스트하는 것이 아니라 단순히 지시문이 각에 "연결된"속성이라는 점에 동의한다.
daniellmb

1
또한 daniellmb, 이것을 테스트하는 방법은 greet함수 를 노출 하고 그것에 대해 감시하고 그것이 호출되었는지 확인하는 것입니다-$ watch가 아닙니다.
Andrew Joslin 2013-07-06

맞습니다, 이것은 인위적인 예이지만 격리 범위를 테스트하는 깨끗한 방법이 있는지 관심이있었습니다. 캡슐화를 끊고 스코프에 메서드를 배치하는 것은이 경우 스파이를 호출하기 전에 추가 할 후크가 없기 때문에 작동하지 않습니다.
daniellmb

@AndyJoslin, 호기심 때문에 isolateScope변수를 만드는 이유 는 무엇입니까? 이 egghead 비디오 ( egghead.io/lessons/angularjs-unit-testing-directive-scope ) 에 대한 Ang의 의견을 참조하십시오 . Angular 1.2부터 격리 된 범위를 검색하려면 code.angularjs.org/1.2element.isolateScope() 대신 사용해야 합니다 element.scope() . 0 / docs / api / angular.element
Danger14

1

로직을 별도의 컨트롤러로 이동합니다.

//will get your isolate scope
function MyCtrl($scope)
{
  //non-DOM manipulating ctrl logic here
}
app.controller(MyCtrl);

function MyDirective()
{
  return {
    scope     : {},
    controller: MyCtrl,
    link      : function (scope, element, attrs)
    {
      //moved non-DOM manipulating logic to ctrl
    }
  }
}
app.directive('myDirective', MyDirective);

컨트롤러와 마찬가지로 후자를 테스트합니다. 스코프 개체를 직접 전달합니다 ( 예는 여기 에서 컨트롤러 섹션 참조 ).

테스트에서 $ watch를 트리거해야하는 경우 다음을 수행하십시오.

describe('MyCtrl test', function ()
{
  var $rootScope, $controller, $scope;

  beforeEach(function ()
  {
    inject(function (_$rootScope_, _$controller_)
    {
      // The injector unwraps the underscores (_) from around the parameter names when matching
      $rootScope = _$rootScope_;
      $controller = _$controller_;
    });

    $scope = $rootScope.$new({});
    $scope.foo = {x: 1}; //initial scope state as desired
    $controller(MyCtrl, {$scope: $scope}); //or by name as 'MyCtrl'
  });

  it('test scope property altered on $digest', function ()
  {
    $scope.$digest(); //trigger $watch
    expect($scope.foo.x).toEqual(1); //or whatever
  });
});

0

격리 범위로 가능할지 모르겠습니다 (누군가가 나를 틀렸다는 것을 증명하길 바랍니다). 지시문에서 생성되는 격리 범위는 격리되어 있으므로 지시문의 $ watch 메서드는 단위 테스트에서 감시하는 범위와 다릅니다. scope : {}를 scope : true로 변경하면 지시문 범위가 프로토 타입 적으로 상속되고 테스트를 통과해야합니다.

때로는 (대부분) 격리 범위가 좋은 것이기 때문에 이것이 가장 이상적인 솔루션은 아니라고 생각합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.