AngularJS'de İzole Kapsam Direktifi Birim Testi Nasıl Yapılır


81

AngularJS'de yalıtılmış kapsamı birim test etmenin iyi bir yolu nedir?

JSFiddle gösteren birim testi

Yönerge pasajı

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

Ben değişiklikler için dinliyor yönergesini sağlamak istiyoruz - bunu yapar değil izole bir kapsamı ile çalışmalarını:

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

GÜNCELLEME: Beklenen izleyicilerin alt kapsama eklenip eklenmediğini kontrol ederek çalışmasını sağladım, ancak çok kırılgan ve muhtemelen erişimcileri belgelenmemiş bir şekilde kullanıyor (başka bir deyişle, önceden haber verilmeksizin değiştirilebilir!).

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

GÜNCELLEME 2: Bahsettiğim gibi bu kırılgan! Fikir hala ama erişir değiştiğini angularjs yeni sürümlerinde çalışır scope()için isolateScope():

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

Casusluğu ayarlamanın bir yolunu buldunuz mu?
tusharmath

@Tushar gerçekten değil, daha önce olduğu gibi çalıştırmanın bir yolu var, ancak önceden haber verilmeksizin değiştirilebilir, bu yüzden kendi sorumluluğunuzdadır kullanın.
daniellmb

Yanıtlar:


102

Açısal eleman api belgelerine bakın . Element.scope () kullanırsanız , direktifinizin kapsam özelliğinde tanımladığınız elemanın kapsamını alırsınız. Eğer kullanırsanız element.isolateScope () tamamını izole kapsamını olsun. Örneğin, direktifiniz şuna benzerse:

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

Ardından, testinizde element.scope () çağrısı geri dönecektir

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

Ancak element.isolateScope () 'u çağırırsanız

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

Bu açısal 1.2.2 veya 1.2.3 itibariyle doğrudur, tam olarak emin değil. Önceki sürümlerde yalnızca element.scope () vardı.


1
v1.2.3 feat (jqLite): isolateScope () alıcıyı kapsama benzer () github.com/angular/angular.js/commit/…
daniellmb

1
ama $ watch yönteminde nerede casusluk yaparsınız?
tusharmath

1
$ watch üzerinde çalışan işlevi açığa çıkarabilir ve ardından casusluk yapabilirsiniz. Yönergede, "kapsam.myfunc = function () ..." olarak ayarlayın, ardından $ watch'da "$ kapsam. $ İzle ('adım', kapsam.İşlev);" yapın. Şimdi testte, izole kapsamdan myFunc alabilir ve üzerinde casusluk yapabilirsiniz.
Yair Tavor

22
Benim için çalışmıyor. element.isolateScope()döner undefined. Ve element.scope()kapsamıma koyduğum her şeyi içermeyen bir kapsam döndürür.
mcv

4
@mcv Yapmam gerektiğini buldumelement.children().isolateScope()
Will Keeling

11

var isolateScope = myDirectiveElement.scope()İzolat kapsamını almak için yapabilirsiniz .

$ Watch'un çağrıldığını gerçekten test etmenize gerek yok .. bu, uygulamanızı test etmekten daha fazla angularjs testi yapmaktır. Ama sanırım bu sadece soru için bir örnek.


2
Bunun "açısal test" olduğunu kabul ettiğimden emin değilim $ watch'un çalıştığını test etmiyorum, sadece direktifin "kablolu" ve açısal özelliği olduğunu test ediyorum.
daniellmb

1
Ayrıca daniellmb, bunu test etmenin yolu, greetişlevinizi ortaya çıkarmak ve bu konuda casusluk yapmak ve bunun $ watch değil, çağrılıp çağrılmadığını kontrol etmektir.
Andrew Joslin

Doğru, bu uydurma bir örnek, ancak kapsamı izole etmenin temiz bir yolu olup olmadığını merak ettim. Casus çağrılmadan önce eklenecek bir kanca olmadığından, kapsüllemeyi kırmak ve kapsam üzerine yöntemler koymak bu durumda işe yaramaz.
daniellmb

@AndyJoslin, Merak ettiğimden, neden bir isolateScopedeğişken oluşturdunuz ? Angular'ın bu egghead video hakkındaki yorumuna bakın ( egghead.io/lessons/angularjs-unit-testing-directive-scope ): Angular 1.2'den itibaren, yalıtılmış kapsamı almak için code.angularjs.org/1.2element.isolateScope() yerine kullanılması element.scope() gerekir. 0 / docs / api /
angular.element

1

mantığı ayrı bir denetleyiciye taşıyın, yani:

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

ve herhangi bir denetleyicide yaptığınız gibi ikincisini test edin - kapsam nesnesini doğrudan içeri aktarın ( bir örnek için buradaki Denetleyiciler bölümüne bakın ).

$ watch'u testinizde tetiklemeniz gerekiyorsa şunları yapın:

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

İzole kapsamla bunun mümkün olduğundan emin değilim (umarım birisi beni yanlış kanıtlar). Direktifte oluşturulan izolat kapsamı izole edilmiştir, bu nedenle direktifteki $ watch yöntemi, birim testinde gözetlediğiniz kapsamdan farklıdır. Kapsamı: {} öğesini kapsam: true olarak değiştirirseniz, yönerge kapsamı prototip olarak devralır ve testleriniz geçmelidir.

Sanırım bu en ideal çözüm değil, çünkü bazen (çoğu zaman) kapsamı izole etmek iyi bir şeydir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.