Cách kiểm tra đơn vị chỉ thị phạm vi cô lập trong AngularJS


81

Cách tốt để kiểm tra đơn vị phạm vi cô lập trong AngularJS là gì

JSFiddle hiển thị đơn vị thử nghiệm

Đoạn mã hướng dẫn

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

Tôi muốn đảm bảo rằng chỉ thị đang lắng nghe các thay đổi - điều này không hoạt động với một phạm vi riêng biệt:

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

CẬP NHẬT: Tôi đã bắt nó hoạt động bằng cách kiểm tra xem những người theo dõi dự kiến ​​đã được thêm vào phạm vi con hay chưa, nhưng nó rất giòn và có thể đang sử dụng những người truy cập theo cách không có giấy tờ (còn có thể thay đổi mà không cần thông báo!).

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

CẬP NHẬT 2: Như tôi đã đề cập, loại này rất giòn! Ý tưởng vẫn hoạt động nhưng trong các phiên bản AngularJS mới hơn, trình truy cập đã thay đổi từ scope()thành isolateScope():

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

Bạn đã tìm ra cách để thiết lập gián điệp?
tusharmath

@Tushar không thực sự, như trước đây có một cách để làm cho nó hoạt động nhưng nó có thể thay đổi mà không cần thông báo trước vì vậy hãy tự chịu rủi ro khi sử dụng.
daniellmb

Câu trả lời:


102

Xem tài liệu api phần tử góc . Nếu bạn sử dụng element.scope (), bạn sẽ nhận được phạm vi của phần tử mà bạn đã xác định trong thuộc tính phạm vi của chỉ thị của bạn. Nếu bạn sử dụng element.isolateScope (), bạn sẽ nhận được toàn bộ phạm vi bị cô lập. Ví dụ: nếu chỉ thị của bạn trông giống như sau:

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

Sau đó, việc gọi element.scope () trong thử nghiệm của bạn sẽ trả về

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

Nhưng nếu bạn gọi element.isolateScope (), bạn sẽ nhận được

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

Điều này đúng với góc 1.2.2 hoặc 1.2.3, không chắc chắn chính xác. Trong các phiên bản trước, bạn chỉ có element.scope ().


1
v1.2.3 feat (jqLite): phơi bày isolateScope () getter tương tự như scope () github.com/angular/angular.js/commit/…
daniellmb

1
nhưng bạn theo dõi phương pháp $ watch ở đâu?
tusharmath

1
bạn có thể để lộ chức năng chạy trên $ watch và sau đó theo dõi nó. Trong chỉ thị, hãy đặt "scope.myfunc = function () ...", sau đó trong $ watch thực hiện "$ scope. $ Watch ('myName', scope.myfunc);". Bây giờ trong thử nghiệm, bạn có thể lấy myFunc từ phạm vi bị cô lập và theo dõi nó.
Yair Tavor

22
Không hiệu quả với tôi. element.isolateScope()lợi nhuận undefined. Và element.scope()trả về một phạm vi không chứa tất cả những thứ mà tôi đặt trên phạm vi của mình.
mcv

4
@mcv tôi thấy mình cần phải làmelement.children().isolateScope()
Will Keeling

11

Bạn có thể làm gì var isolateScope = myDirectiveElement.scope()để có được phạm vi cô lập.

Mặc dù vậy, bạn không thực sự cần phải kiểm tra xem $ watch đó .. đó là thử nghiệm góc cạnh nhiều hơn thử nghiệm ứng dụng của bạn. Nhưng tôi đoán nó chỉ là một ví dụ cho câu hỏi.


2
Tôi không chắc tôi đồng ý rằng đó là "thử nghiệm góc cạnh" Tôi không kiểm tra xem $ watch hoạt động mà chỉ đơn giản rằng chỉ thị là thuộc tính "có dây" thành góc cạnh.
daniellmb

1
Cũng daniellmb, cách để kiểm tra điều này sẽ là để lộ greetchức năng của bạn và theo dõi điều đó, và kiểm tra xem nó có được gọi là - không phải là $ watch.
Andrew Joslin

Đúng, đây là một ví dụ có sẵn, nhưng tôi quan tâm nếu có một cách sạch sẽ để kiểm tra phạm vi cô lập. Việc phá vỡ tính năng đóng gói và đưa các phương thức vào phạm vi sẽ không hoạt động trong trường hợp này vì không có móc để thêm gián điệp trước khi nó được gọi.
daniellmb

@AndyJoslin, Vì tò mò, tại sao lại tạo ra một isolateScopebiến? Xem bình luận của Ang về video egghead này ( egghead.io/lessons/angularjs-unit-testing-directive-scope ): Kể từ Angular 1.2, để truy xuất phạm vi bị cô lập, người ta cần sử dụng element.isolateScope()thay vì element.scope() code.angularjs.org/1.2. 0 / docs / api / angle.element
Danger

1

chuyển logic sang một bộ điều khiển riêng biệt, tức là:

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

và kiểm tra phần sau như bạn làm với bất kỳ bộ điều khiển nào - chuyển trực tiếp đối tượng phạm vi vào (xem phần Bộ điều khiển ở đây để làm ví dụ).

nếu bạn cần kích hoạt $ watch trong thử nghiệm của mình, hãy làm:

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

Tôi không chắc nó có thể với phạm vi cô lập (mặc dù tôi hy vọng ai đó chứng minh tôi sai). Phạm vi cô lập được tạo trong chỉ thị, tốt, là cô lập, vì vậy phương thức $ watch trong chỉ thị khác với phạm vi mà bạn đang theo dõi trong bài kiểm tra đơn vị. Nếu bạn thay đổi phạm vi: {} thành phạm vi: true, phạm vi chỉ thị sẽ kế thừa theo nguyên mẫu và các thử nghiệm của bạn sẽ vượt qua.

Tôi đoán đây không phải là giải pháp lý tưởng nhất, bởi vì đôi khi (rất nhiều lần), cô lập phạm vi là một điều tốt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.