4

我是单元测试以及 ng-animate 模块的新手。我做了一个简单的指令来测试 ng-animate。

 .directive('slideShow', function ($animate, $compile) {
    return {
      template: '<div class="slide-show-container"></div>',
      restrict: 'EA',
      replace: true,
      link: function (scope, element, attrs) {
        var newElement = $compile('<div class="slide-show-slide"></div>')(scope);

        element.bind('mouseenter',function() {
          element.append(newElement);
          $animate.addClass(newElement, 'slide-enter');
        });
        element.bind('mouseleave',function() {
          $animate.removeClass(newElement, 'slide-enter');
        });
      }
    };
  });

然后我进行了以下单元测试以确认正在添加 .slide-enter 类。

  it('should add slide-enter class', function () {
    element.triggerHandler( "mouseenter" );
    expect(element.children().hasClass("slide-enter")).toEqual(true)
  });

当我在手动测试中将鼠标悬停在该指令上时,该指令正确添加了该类。但是,单元测试失败并显示未添加 slide-enter 类。

最后我想出了解决它的唯一方法是将单元测试包装在 $timeout 中:

  it('should add slide-enter class', inject(function ($timeout) {
    element.triggerHandler( "mouseenter" );
    $timeout(function() {
      expect(element.children().hasClass("slide-enter")).toEqual(true);
    });
    $timeout.flush();
  }));

谁能帮我理解为什么测试需要这个 $timeout 才能工作?还有另一种方法可以让我搞砸的这个单元测试工作吗?

4

1 回答 1

4

注意我正在使用 angular-animate 1.2.0-rc.2 并且已经用这个版本记录了我的发现。查看 1.2.0-rc.3 代码时,调用的需求$timeout.flush()似乎已得到解决,但我尚未对其进行测试。https://github.com/angular/angular.js/blob/v1.2.0-rc.3/src/ngAnimate/animate.js


我的一项测试遇到了同样的问题。在我调用$timeout.flush()了应该触发添加类的代码之后并且在调用expect. 如果你像这样重写它,你的测试应该可以工作:

it('should add slide-enter class', inject(function ($timeout) {
  element.triggerHandler( "mouseenter" );
  $timeout.flush();  
  expect(element.children().hasClass("slide-enter")).toEqual(true);
}));

我不得不深入研究 ngAnimate 代码才能弄清楚,这就是我发现的。

如果你看一下函数中的angular-animate.js文件。addClass您将看到以下内容:

addClass : function(element, className, done) {
  performAnimation('addClass', className, element, null, null, function() {
    $delegate.addClass(element, className, done);
  });
}

作为最后一个参数的闭包performAnimation将最终添加该类。

performAnimation中,最后一个参数被命名为“onComplete”。当动画应该被跳过时,有一段代码处理调用这个闭包:

//skip the animation if animations are disabled, a parent is already being animated
//or the element is not currently attached to the document body.
if ((parent.inheritedData(NG_ANIMATE_STATE) || disabledAnimation).running) {
  //avoid calling done() since there is no need to remove any
  //data or className values since this happens earlier than that
  //and also use a timeout so that it won't be asynchronous
  $timeout(onComplete || noop, 0, false);
  return;
}   

并且有调用$timeout导致问题。在角度测试中运行此代码时,对 的调用$timeout只是将闭包排队。然后必须调用测试代码$timeout.flush()才能运行该函数。

于 2013-10-17T16:11:45.753 回答