39

我正在尝试使用 Karma 用 Ja​​smine 测试我的 AngularJS 控制器。但是$timeout在现实生活中运行良好的 a 会使我的测试崩溃。

控制器:

var Ctrl = function($scope, $timeout) {
  $scope.doStuff = function() {
    $timeout(function() {
      $scope.stuffDone = true;
    }, 250);
  };
};

Jasmine it 阻塞(其中$scope和控制器已正确初始化):

it('should do stuff', function() {
  runs(function() {
    $scope.doStuff();
  });
  waitsFor(function() { 
    return $scope.stuffDone; 
  }, 'Stuff should be done', 750);
  runs(function() {
    expect($scope.stuffDone).toBeTruthy();
  });
});

当我在浏览器中运行我的应用程序时,$timeout函数将被执行并且$scope.stuffDone为真。但在我的测试中,$timeout什么也没做,函数永远不会执行,并且 Jasmine 在超时 750 毫秒后报告错误。这里可能有什么问题?

4

3 回答 3

84

根据$timeout的 Angular JS 文档,您可以使用它$timeout.flush()来同步刷新延迟函数的队列。

尝试将您的测试更新为:

it('should do stuff', function() {
  expect($scope.stuffDone).toBeFalsy();
  $scope.doStuff();
  expect($scope.stuffDone).toBeFalsy();
  $timeout.flush();
  expect($scope.stuffDone).toBeTruthy();
});

这是一个plunker,显示您的原始测试失败和新测试通过。

于 2013-06-29T22:37:58.927 回答
10

正如其中一条评论所指出的,setTimeout没有使用 Jasmine 模拟,因为使用了 Angular 的 JS 模拟$timeout服务。就个人而言,我宁愿使用 Jasmine 的,因为它的模拟方法让我可以测试超时的长度。您可以在单元测试中使用简单的提供程序有效地规避它:

module(function($provide) {
  $provide.constant('$timeout', setTimeout);
});

注意:如果你走这条路线,一定要$scope.apply()在 jasmine.Clock.tick 之后调用。

于 2013-10-23T04:52:28.823 回答
4

由于 $timeout只是一个包装器window.setTimeout,您可以使用 jasminesClock.useMock()来模拟window.setTimeout

  beforeEach(function() {
    jasmine.Clock.useMock();
  });

  it('should do stuff', function() {
    $scope.doStuff();
    jasmine.Clock.tick(251);
    expect($scope.stuffDone).toBeTruthy();
  });
于 2013-07-03T12:28:49.977 回答