81 votes

Comment l'Unité de Test Isolé Champ d'application de la Directive dans AngularJS

Ce qui est un bon moyen de l'unité de test isolé portée en AngularJS

JSFiddle montrant l'unité de test

Extrait de la Directive

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

Je veux m'assurer que la directive est à l'écoute de modifications - ce qui ne veut pas travailler avec un cas isolé portée:

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

Mise à JOUR: Je l'ai eu à travailler en vérifiant si la durée d'observateurs ont été ajoutés à l'enfant, mais il est très fragile et probablement en utilisant les accesseurs sans papiers moyen (aka sujets à changement sans préavis!).

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

Mise à JOUR 2: Comme je l'ai mentionné c'est fragile! L'idée fonctionne toujours, mais dans des versions plus récentes de AngularJS l'accesseur a changé à partir de scope() de isolateScope():

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

102voto

Yair Tavor Points 717

Voir angulaire de l'élément de l'api docs. Si vous utilisez l' élément.portée() vous obtenez l'élément du champ d'application que vous avez définie dans le champ d'application de la propriété de votre directive. Si vous utilisez l' élément.isolateScope() vous obtenez l'ensemble isolé portée. Par exemple, si votre directive ressemble à quelque chose comme ceci :

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

Puis l'appel de l'élément.portée() dans votre test sera de retour

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

Mais si vous appelez de l'élément.isolateScope (), vous obtiendrez

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

C'est vrai que du moment cinétique 1.2.2 ou 1.2.3, ne sais pas exactement. Dans les versions précédentes vous aviez seul élément.champ d'application().

11voto

Andy Joslin Points 23231

Vous pouvez le faire var isolateScope = myDirectiveElement.scope() pour obtenir de l'isoler de la portée.

Vous n'avez pas vraiment besoin de tester que $watch a appelé mais.. c'est plus de tests angularjs que les tests de votre application. Mais je suppose que c'est juste un exemple pour la question.

1voto

Nikita Points 1387

déplacer la logique à un contrôleur séparé, c'est à dire:

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

et test-ci comme vous le feriez pour n'importe quel contrôleur de transmission de la portée de l'objet directement (voir les Contrôleurs de la section ici pour un exemple).

si vous avez besoin de déclencher $regarder dans votre test à faire:

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

0voto

UnicodeSnowman Points 811

Je ne suis pas sûr que c'est possible avec les isoler de la portée (même si j'espère que quelqu'un prouve que j'ai tort). L'isolat de la portée qui sera créé dans la directive est, bien isolé, de sorte que le $regarder méthode dans la directive est différente de la portée de ce que vous êtes d'espionnage dans l'unité de test. Si vous modifiez la portée: {} à portée: true, le champ d'application de la directive héritera de fait et vos tests doivent passer.

Je suppose que ce n'est pas la meilleure solution, parce que parfois (beaucoup de temps), isoler le champ d'application est une bonne chose.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X