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

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');



답변

각도 요소 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 () 만있었습니다.


답변

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

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


답변

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

//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
  });
});


답변

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

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


답변