【问题标题】:jasmine.clock().tick() does not work with $timeout and debounce, but works fine with setTimeoutjasmine.clock().tick() 不适用于 $timeout 和 debounce,但适用于 setTimeout
【发布时间】:2014-09-24 11:43:45
【问题描述】:

下面我有 3 个功能完全相同。每个人使用不同的调用方式setTimeoutdelay1() 直接使用setTimeoutdelay2() 使用angularjs $timeoutdelay3() 使用lodash debounce。它们都工作正常。

当我使用 Jasmine 进行测试时出现问题。 setTimeoutjasmine.clock().tick() 方法可以正常工作,但 $timeoutdebounce 不能

我有兴趣与 Jasmine 一起使用 debounce。我知道我可以将 $timeout.flush() 与 angularjs 一起使用,但 $timeoutsetTimeout 在我将其与传单地图一起使用的代码中的其他地方给我带来了问题。 debounce 与传单很好地配合使用。

我在这里创建了一个 plunker:plnkr,您将在其中看到 $timeout 和去抖测试未通过,而 setTimeout 测试通过。

有没有办法解决这个问题?谢谢

JS

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope, $timeout) {
  $scope.name = 'World';
  $scope.delayed1 = function(){
    setTimeout(function(){
      $scope.name = "Hello world by setTimeout";
    },500)
  }
  $scope.delayed2 = function(){
    $timeout(function(){
      $scope.name = "Hello world by $timeout";
    },500)
  }
  $scope.delayed3 = function(){
    _.debounce(function(){
      $scope.name = "Hello world by debounce";
    },500)
  }
});

规格

describe('Testing a Hello World controller', function() {
  var $scope = null;
  var ctrl = null;

  //you need to indicate your module in a test
  beforeEach(module('plunker'));

  beforeEach(inject(function($rootScope, $controller) {
    $scope = $rootScope.$new();

    ctrl = $controller('MainCtrl', {
      $scope: $scope
    });
  }));

  it('should say hallo to the World', function() {
    expect($scope.name).toEqual('World');
  });
  it('should say Hello world by setTimeout', function() {
    jasmine.clock().install();
    $scope.delayed1();
    jasmine.clock().tick(600);
    expect($scope.name).toEqual('Hello world by setTimeout');
    jasmine.clock().uninstall();

  });
  it('should say Hello world by timeout', function() {
    jasmine.clock().install();
    $scope.delayed2();
    jasmine.clock().tick(600);
    expect($scope.name).toEqual('Hello world by timeout');
    jasmine.clock().uninstall();

  }); 
  it('should say Hello world by debouce', function() {
    jasmine.clock().install();
    $scope.delayed3();
    jasmine.clock().tick(600);
    expect($scope.name).toEqual('Hello world by debouce');
    jasmine.clock().uninstall();

  }); 
});

【问题讨论】:

  • 对此有什么结论吗?
  • 不,还没有。有人吗?

标签: javascript angularjs jasmine leaflet lodash


【解决方案1】:

只有当您直接测试 setInterval()setTimeout() 函数时,Jasmine 中的时钟才会起作用,因为它只是专门模拟这些函数以同步运行。我相信 Jasmine 有一个拉取请求来模拟 Date 对象,这将允许在不模拟的情况下测试 _.debounce() 之类的函数,但我不记得它是否被合并。

要测试_.debounce(),您必须模拟它以同步运行,最好是作为间谍,或者您可以直接覆盖该函数。以下是我发现的适合我的方法:

spyOn(_, 'debounce').and.callFake(function (func) {
    return function () {
        func.apply(this, arguments);
    };
});

现在对_.debounce() 的调用将同步运行,测试应该会成功完成。当然你还是得使用$timeout.flush()

我用这些更改更新了你的 plunker:http://plnkr.co/edit/KXmwcf1faUNf8nlqPeyd

【讨论】:

  • 感谢 Sedushi 的工作。我一定会记住这个技巧
  • 太棒了,之前一直在敲我的头,但最后这个成功了。
  • 很好的解决方案!我可以添加一些语法糖吗:spyOn(_, 'debounce').and.callFake(_.identity);
  • 这里有一个边缘案例问题。如果你有一个带有去抖动功能的手表,并以这种方式模拟去抖动,那么当你调用触发观察者所需的 $scope.$apply() 时,如果有一个 $scope.$apply() 在debounced 函数然后我们会得到一个错误,因为我们仍然处于触发它的摘要循环中。我在想,理想情况下,我们最好用一个可以立即解决超时的函数来模拟它,但我还没有让它工作。 :/ 真讨厌!
  • 聪明的解决方案+1
【解决方案2】:

lodash 的 debounce 函数使用 Date 对象。您可以像这样使用 jasmine 模拟 Date 对象:

jasmine.clock().install();
jasmine.clock().mockDate();

jasmine.clock().tick(1000); // trigger the debounce

来源:https://jasmine.github.io/2.9/introduction.html#section-Mocking_the_Date

【讨论】:

    【解决方案3】:

    除了@JDWardle 的回答之外,您可能还想为 debounce cancel 方法创建一个间谍。

    spyOn(_, 'debounce').and.callFake(function (func) {
        var mockDebounce = function () {
            func.apply(this, arguments);
        };
        mockDebounce.cancel = jasmine.createSpy('cancel');
        return mockDebounce;
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-02
      • 1970-01-01
      • 2012-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多