简体   繁体   English

如何使用Jasmine,AngularJs测试_.defer()

[英]How to test _.defer() using Jasmine, AngularJs

I already asked this question where the main point was scope doesn't exists in terminal but it exists in Chrome debugging tool. 我已经问过这个问题 ,主要观点是终端中不存在范围,但它存在于Chrome调试工具中。 Despite the answers it didn't get fixed. 尽管有答案但它没有得到修复。

The question is what is the right syntax to test the bellow directive, especially the line expect(scope.measurementScroll).toBe(true); 问题是测试波纹管指令的正确语法是什么,特别是线路expect(scope.measurementScroll).toBe(true); . While digging through web i couldn't find any similar question most questions are related to $q.defer() where in my case there is underscore method _.defer() 在挖掘网页时我找不到任何类似的问题,大多数问题都与$q.defer() ,在我的情况下,有下划线方法_.defer()

Controller 调节器

'use strict';
angular.module('myApp')
  .controller('MeasurementsTimelineCtrl', ['$scope', '$state', 'Measurements', function($scope, $state, Measurements) {
    $scope.measurements = null;
    var userId = $scope.currentUser ? $scope.currentUser.id : null;
    if (userId) {
      var listOfMeasurements = Measurements.userIndex(userId);
      listOfMeasurements.then(function(data){
        $scope.measurements = data;
        $scope.$broadcast('measurements-updated', $scope.measurements);
      });
    }
  }]);

Directive: 指示:

'use strict';
angular.module('myApp')
  .directive('dashboardMeasurementTimeline', ['$window', function($window) {
    return {
      restrict: 'E',
      templateUrl: 'myView.html',
      controller: 'MeasurementsTimelineCtrl',
      link: function(scope, element){
        scope.$on('measurements-updated', function(measurements) {
          _.defer(function(){
            if(measurements) {
              scope.measurementScroll = true;
            }
          });
        });
      }
    };
  }]);

Test 测试

'use strict';
describe('Directive: dashboardMeasurementTimeline', function () {

  var $rootScope, $compile, element, scope;

  beforeEach(function() {
    module('myApp');

    inject(function($injector) {
      $rootScope = $injector.get('$rootScope');
      $compile = $injector.get('$compile');
    });

    scope = $rootScope.$new();
    element = angular.element('<dashboard-measurement-timeline></dashboard-measurement-timeline>');
    element = $compile(element)(scope);

    scope.currentUser = {id : 'someId'};
    scope.$digest();
    scope.measurements = [{id: 'someId', time_of_test: 'Tue, 30 Dec 2014 14:00:00 -0000'},
      {id: 'someId', time_of_test: 'Thu, 20 Nov 2014 03:00:00 -0000'},];
    scope.$broadcast('measurements-updated', scope.measurements);
    scope.$apply();
  });

  it('should assign true value to measurementScroll', function () {
    expect(scope.measurementScroll).toBe(true);
  });
});

You can do this by injecting a mock underscore library, with a defer function defined in the test. 您可以通过注入模拟下划线库来执行此操作,并在测试中定义defer函数。 A way to do this is to define your own factory, _ , which can then be mocked easily: 一种方法是定义自己的工厂_ ,然后可以轻松地进行模拟:

app.factory('_', function($window) {
  return $window._;
});

Then in the directive, you have to use it by injecting it: 然后在指令中,你必须通过注入它来使用它:

app.directive('dashboardMeasurementTimeline', ['_', function(_) {

In the test, you can then mock it: 在测试中,您可以模拟它:

var deferCallback;
beforeEach(module(function($provide) {
  deferCallback = null;
  $provide.value('_', {
    defer: function(callback) {
      deferCallback = callback;
    }
  });
}));

This means that instead of the real one, the directive will use the mock _ , which saves the callback passed to defer as deferCallback so you can invoke it when needed: 这意味着该指令将使用mock _ ,而不是真正的指令,它将传递给defer的回调保存为deferCallback因此您可以在需要时调用它:

scope.$broadcast('measurements-updated', scope.measurements);
deferCallback();

This makes the test synchronous, which is usually a better idea than using done() , as it keeps test as fast as possible. 这使得测试同步,这通常比使用done()更好,因为它尽可能快地保持测试。

You can see the above working at http://plnkr.co/edit/r7P25jKzEFgE5j10bZgE?p=preview 你可以在http://plnkr.co/edit/r7P25jKzEFgE5j10bZgE?p=preview看到上面的工作

If you do not have lodash as a service to be injected you can just spy over the defer method, and if you care about the execution of the function passed then you can just set a callFake and call the argument function passed to the real defer : 如果你没有lodash作为要注入的服务,你可以只监视defer方法,如果你关心传递的函数的执行,那么你可以设置一个callFake并调用传递给real defer的参数函数:

spyOn(_, 'defer').and.callFake(f => f());

More deeper let's say you have the following call: 更深入的让我们说你有以下电话:

function toTest() {
 _.defer(() => service.callAFunction());
}

then in your test you can say: 然后在你的测试中你可以说:

it('should call service.callAFunction', () => {
   spyOn(service, 'callAFunction');
   spyOn(_, 'defer').and.callFake(f => f());
   toTest();
   expect(_.defer).toHaveBeenCalled();
   expect(service.callAFunction).toHaveBeenCalled();
}

@Michal Charezma, gave a great solution for the problem that is actually a solution, but as it turned out it has some other restrictions for the rest of _ functions. @Michal Charezma为这个实际上是解决方案的问题提供了一个很好的解决方案,但事实证明它对其余的_函数有一些其他的限制。 For example: 例如:

angular.element(scrollContainer).bind('scroll', _.throttle(scope.disableButtons, 500));

raises an error that the throttle is undefined. 引发一个错误,即throttle未定义。

Following @Michal's logic, found another solution that lets functions like _.throttle() to work properly. 遵循_.throttle()的逻辑,找到了另一个让_.throttle()等函数正常工作的解决方案。 So, instead of importing _ and using: 所以,而不是导入_并使用:

app.factory('_', function($window) {
  return $window._;
});

One can mock the defer function only, from the spec like: 人们可以只根据以下规范来模拟defer函数:

var deferCallback = $window._.defer.mostRecentCall.args[0];
deferCallback()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM