Come testare il campo di applicazione della direttiva isolata in AngularJS

Che cosa è un buon metodo per eseguire il test di un ambito isolato in AngularJS

JSFiddle mostra il test dell’unità

Frammento di direttiva

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

Voglio assicurarmi che la direttiva stia ascoltando le modifiche – questo non funziona con un ambito isolato:

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

AGGIORNAMENTO: L’ ho fatto funzionare controllando se gli osservatori attesi sono stati aggiunti all’oscilloscopio secondario, ma è molto fragile e probabilmente utilizza gli accessor in modo non documentato (ovvero sobject a modifiche senza preavviso!).

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

AGGIORNAMENTO 2: Come ho detto, questo è fragile! L’idea funziona ancora, ma nelle versioni più recenti di AngularJS l’accessor è stato modificato da scope() a isolateScope() :

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

Vedi documenti api con elementi angolari . Se si utilizza element.scope () si ottiene l’ambito dell’elemento definito nella proprietà scope della direttiva. Se si utilizza element.isolateScope () si ottiene l’intero ambito isolato. Ad esempio, se la tua direttiva assomiglia a qualcosa del genere:

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

Quindi chiamerà element.scope () nel test

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

Ma se chiami element.isolateScope () otterrai

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

Questo è vero a partire dall’angular 1.2.2 o 1.2.3, non esattamente sicuro. Nelle versioni precedenti avevi solo element.scope ().

È ansible eseguire var isolateScope = myDirectiveElement.scope() per ottenere l’ambito isolato.

Non hai davvero bisogno di testare che sia stato chiamato $ watch … è più test angularjs che testare la tua app. Ma immagino sia solo un esempio per la domanda.

spostare la logica in un controller separato, ad es .:

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

e prova quest’ultimo come qualsiasi controller, passando direttamente l’object scope (vedi la sezione Controller qui per un esempio).

se devi triggersre $ watch nel tuo test, procedi nel seguente modo:

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

Non sono sicuro che sia ansible con un ambito isolato (anche se spero che qualcuno mi provi male). L’ambito isolato che viene creato nella direttiva è, beh, isolato, quindi il metodo $ watch nella direttiva è diverso dall’ambito che si sta spiando nel test dell’unità. Se si modifica l’ambito: {} su scope: true, l’ambito della direttiva verrà ereditato in modo prototipico e i test dovrebbero passare.

Immagino che questa non sia la soluzione ideale, perché a volte (un sacco di tempo) isolare la portata è una buona cosa.