jasmine.clock().tick()不能与$timeout和debounce一起工作,但可以与setTimeou

jasmine.clock().tick() does not work with $timeout and debounce, but works fine with setTimeout

本文关键字:工作 一起 但可以 setTimeou debounce tick clock 不能 jasmine timeout      更新时间:2023-09-26

下面我有3个函数做完全相同的事情。每个都使用不同的方式调用setTimeout, delay1()直接使用setTimeout, delay2()使用angularjs $timeout, delay3()使用lodash debounce。它们都很好。

当我使用Jasmine进行测试时,问题出现了。setTimeout可以很好地使用jasmine.clock().tick()方法,但$timeoutdebounce不能

我有兴趣和Jasmine一起工作。我知道我可以使用$timeout.flush()与angularjs,但$timeoutsetTimeout给我的问题在我的代码的其他地方,我使用它与传单地图。《Debounce》与传单搭配得很好。

我在这里创建了一个plunker: plnkr,在这里您将看到$timeout和debounce测试没有通过,而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();
  }); 
});

Jasmine中的时钟仅在您直接测试setInterval()setTimeout()函数时才会工作,因为它只是专门模拟这些函数以同步运行。我相信有一个pull request for Jasmine来模拟Date对象,这将允许测试像_.debounce()这样的函数而不模拟它,但我不记得是否合并在一起。

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

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

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

我更新了你的活塞与这些变化:http://plnkr.co/edit/KXmwcf1faUNf8nlqPeyd

来自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

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

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