2013-10-01 4 views
2

Короткая версия: У меня есть директива, которая использует «новую область» (то есть, scope:true; не изолят сфера), которая, как представляется, прекрасно работать, когда я взаимодействовать с приложением, но не появляются, чтобы обновить scope наблюдаемым способом в модульных тестах. Зачем вносить изменения в field по направлению директивы не, наблюдаемый в модульном тесте?Единичное тестирование AngularJS Директивы: не обновляемые области?

Пример в Plunker: http://plnkr.co/edit/gpzHsX?p=preview

Вот директива:

angular.module('app').directive('ngxBlurry', [ 
    function() { 
    return { 
     restrict:'C', 
     replace:false, 
     scope:true, 
     link: function(scope, element, attrs) { 
     element.bind('blur', function() { 
      var val = element.val(); 
      scope.$apply(function(scope) { 
      scope.field = val; 
      }); 
     }); 

     scope.$watch('field', function(n, o) { 
      if (n !== o) { 
      scope.fields[n] = scope.fields[o]; 
      delete scope.fields[o]; 
      } 
     }); 
     } 
    }; 
    } 
]); 

Он привыкает примерно следующим образом:

<div ng-controller="FooContoller"> 
    <div ng-repeat="(field, value) in fields"> 
    <input value="{{field}}" type="text" class="ngx-blurry"/> 
    <strong>"field" is &laquo;{{field}}&raquo;</strong> 
    </div> 
</div> 

Предполагая, что fields выглядит примерно так:

$scope.fields = { 
    'foo':true, 
    'bar':true, 
    'baz':true, 
    '':true 
}; 

А вот тестовый модуль:

describe('ngxBlurry', function() { 
    var scope, 
     element, 
     template = '<input value="{{field}}" type="text" class="ngx-blurry"/>'; 

    beforeEach(module('app')); 
    beforeEach(inject(function($rootScope, $compile) { 
    scope = $rootScope.$new(); 
    scope.fields = {'foo':true, '':true}; 
    scope.field = ''; 

    element = $compile(template)(scope); 
    })); 

    it('defers update until after blur', function() { 
    spyOn(scope, '$apply'); 

    element.val('baz'); 
    element.triggerHandler('blur'); 

    // true: 
    expect(scope.$apply).toHaveBeenCalled(); 
    // false!? 
    expect(scope.field).toBe('baz'); 

    scope.$digest(); 
    // still false: 
    expect(scope.field).toBe('baz'); 

    element.scope().$digest(); 
    // *still* false: 
    expect(scope.field).toBe('baz'); 
    // also false: 
    expect(element.scope().field).toBe('baz'); 
    }); 
}); 

Теперь при взаимодействии с приложением:

  1. Я могу ввести в input и любые обновления ключей в $scope.fields не откладывается до размытия событие в этом поле.
  2. В тесте шпион жасмин сообщает, что призыв к $apply происходит но ...
  3. ... функция, которая должна быть выполнена в контексте этого $apply является (видимо) не вызывался ,
  4. ... и не вызывается выражение $watch.

Сама директива в контексте запущенного приложения работает нормально, но я не могу найти причин, по которым я не могу наблюдать за изменениями с помощью единичного теста.

Запрет на редизайн директивы (например, использование области изоляции и событий $emit): есть ли что-нибудь, что я пропустил здесь? Что-то мне нужно изменить по поводу директивы? или какой-то трюк, чтобы сделать эти изменения наблюдаемыми в модульном тесте?

ответ

3

Вкратце: не забудьте указать .andCallThrough() на шпиона.

ничего не меняя о самой директивы, рабочая версия теста должно быть:

// everything about the `describe` stays the same... 
it('defers update until after blur', function() { 
    spyOn(scope, '$apply').andCallThrough(); 

    element.val('baz'); 
    element.triggerHandler('blur'); 

    expect(scope.$apply).toHaveBeenCalled(); 
    expect(element.scope().field).toBe('baz'); 
}); 

Итак ...

  1. Жасмин spy необходимо andCallThrough()
  2. Использование element.scope() для доступ к правильной области.
Смежные вопросы