一尘不染

带有Jasmine的单元测试Angularjs指令(包含私有超时)

angularjs

我有一条指令,该指令的行为应有所不同,具体取决于自初始化以来经过的时间:

am.directive('showText', () => ({
  restrict: 'E',
  replace: true,
  scope: {
    value: '@'
  },
  controller: ($scope, $timeout) => {
    console.log('timeout triggered');

    $scope.textVisible = false;

    let visibilityCheckTimeout = $timeout(() => {
      if (parseInt($scope.value, 10) < 100) {
        $scope.textVisible = true;
      }
    }, 330);

    // Clear timeout upon directive destruction
    $scope.$on('$destroy', $timeout.cancel(visibilityCheckTimeout));
  },
}));

问题是,当我尝试使用Jasmine进行测试时,我似乎找不到以任何方式触发此超时的方法。已经尝试了$timeout.flush()$timeout.verifyNoPendingTasks()(如果我要注释flush调用,实际上会引发错误)。但是它仍然没有触发超时的回调执行

describe('showText.', () => {
  let $compile;
  let $rootScope;
  let $scope;
  let $timeout;

  const compileElement = (rootScope, value = 0) => {
    $scope = rootScope.$new();
    $scope.value = value;

    const element = $compile(`
      <show-text
        value="value"
      ></show-text>
    `)($scope);

    $scope.$digest();

    return element;
  };

  beforeEach(() => {
    module('app.directives.showText');

    inject((_$compile_, _$rootScope_, _$timeout_) => {
      $compile = _$compile_;
      $rootScope = _$rootScope_;
      $timeout = _$timeout_;
    });
  });

  it(`Process lasts > 0.33s. Should show text.`, () => {
    const VALUE = 30;
    const element = compileElement($rootScope, VALUE);
    const elementContent = element.find('.show-text__content');

    $timeout.flush(1000);

    $timeout.verifyNoPendingTasks();

    $rootScope.$digest();

    expect(element.isolateScope().textVisible).toBeTruthy();
    expect(elementContent.length).toEqual(1);
    expect(elementContent.text().trim()).toBe('Example text');
  });
});

测试失败。

找不到我在做什么错。关于如何正确测试这种情况的任何提示?

谢谢。

UPD
经过一番调查,我发现在此特定的测试用例中,在compileElement功能上,服务value没有对属性进行评估$compile。和等于"value"。我已经使用同一个函数十次了,但无法$scope.value理解,为什么它不像以前那样具有的属性。


阅读 295

收藏
2020-07-04

共1个答案

一尘不染

发生这种情况的原因是$timeout.cancel(visibilityCheckTimeout)无条件立即启动。相反,它应该是

$scope.$on('$destroy', () => $timeout.cancel(visibilityCheckTimeout));

可以做一些事情来提高可测试性(除了在$timeout这里充当一次性示波器观察员并要求用它代替之类的事实之外)。

$timeout可以成功[\监视\:

beforeEach(module('app', ($provide) => {
  $provide.decorator('$timeout', ($delegate) => {
    var timeoutSpy = jasmine.createSpy().and.returnValue($delegate);
    angular.extend(timeoutSpy, $delegate);
    spyOn(timeoutSpy, 'cancel').and.callThrough();
    return timeoutSpy;
  });
}));

私有$timeout回调可以暴露给作用域。

$scope._visibilityCheckHandler = () => {
  if (parseInt($scope.value, 10) < 100) {
    $scope.textVisible = true;
  }
};
$timeout($scope._visibilityCheckHandler, 330);

这样,可以监听所有呼叫并获得全部覆盖:

let directiveScope;
...

const element = $compile(`...`)($scope);

directiveScope = element.isolateScope();
spyOn(directiveScope, '_visibilityCheckHandler').and.callThrough();

$scope.$digest();
...

expect($timeout).toHaveBeenCalledWith(directiveScope._visibilityCheckHandler, 330);
expect($timeout.cancel).not.toHaveBeenCalled();

在这种情况下,这里不需要为带有flush延迟参数的’> =
\0.33s’和’<0.33s’制定单独的规范,其$timeout内部工作已经在Angular规范中进行了测试。此外,回调逻辑可以与$timeout规范分开进行测试。

2020-07-04