单元测试 Angularjs 指令,其中包含私有超时,与 Jasmine [英] Unit Test Angularjs directive, which contains private timeout, with Jasmine

查看:19
本文介绍了单元测试 Angularjs 指令,其中包含私有超时,与 Jasmine的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a directive, which should behave differently, depending on how much time passed since it's initialization:

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,它实际上会抛出错误称呼).但它仍然没有触发超时的回调执行

The problem is, that when I'm trying to test it with Jasmine, I can't seem to find a way to trigger this timeout in any way. Already tried a $timeout.flush() and $timeout.verifyNoPendingTasks() (which actually throws an error, if I'll comment flush call). But it's still not triggering that timeout's callback execution

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');
  });
});

测试失败.

找不到我做错了什么.有关如何正确测试此类案例的任何提示?

Can't find what am I doing wrong. Any tips on how to properly test such a case?

谢谢.

UPD经过一番调查,我发现在这个特定的测试用例中,在 compileElement 函数中,value 属性没有被 $compile 服务.并且等于 "value".我已经使用相同的函数 10 次了,但无法理解,为什么它不像以前那样采用 $scope.value 的属性.

UPD After some investigation, I've found that in this particular test-case, in compileElement function, value property isn't being evaluated by $compile service. And equals "value". I've used same function already 10th of times, and can't get, why it's not taking a $scope.value's property as it was before.

推荐答案

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

The reason why this happens is that $timeout.cancel(visibilityCheckTimeout) launches unconditionally and immediately. Instead, it should be

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

可以采取一些措施来提高可测试性(除了 $timeout 在这里作为一次性作用域观察者并要求替换为一个作用域观察者这一事实之外).

There are things that can be done to improve testability (besides the fact that $timeout works here as one-time scope watcher and asks to be replaced with the one).

$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 回调可以暴露给作用域.

Private $timeout callback can be exposed to scope.

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

这样所有的电话都可以被监视并获得全面覆盖:

This way all of the calls can be spied and get full coverage:

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();

在这种情况下,不需要为 '>= 0.33s' 和 '<;0.33s' 和 flush 延迟参数,$timeout 的内部工作已经在 Angular 规范中进行了测试.此外,回调逻辑可以与 $timeout 规范分开测试.

In this case here's no need to have separate specs for '>= 0.33s' and '< 0.33s' with flush delay argument, $timeout's inner work was already tested in Angular specs. Also, callback logic can be tested separately from $timeout spec.

这篇关于单元测试 Angularjs 指令,其中包含私有超时,与 Jasmine的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆