如何使用jasmine测试使用setInterval的角度服务?

时间:2017-03-22 16:10:19

标签: javascript angular timer jasmine

我正在尝试设置setInterval调用的测试。 setInterval总是创建一个新的计时器,所以无论你向前移动tick(),这条消息总会出错:

错误:1个计时器仍然在队列中。

如果我用setInterval覆盖setTimeout函数,则测试按预期传递。我已经使用通常设置为setInterval的公共属性myTimerFunc添加了代码以允许我的角度服务。测试代码将其设置为setTimeout。

有没有更好的方法来测试setInterval?

如果队列中的最后一个计时器被jasmine忽略,那将是很好的。然后我可以毫不费力地进行测试。

例如,除非取消注释覆盖setInterval的行,否则此代码将失败。我不介意在测试代码中执行此操作,但我确实希望避免在正常代码中添加额外的内容以允许此覆盖。

import { fakeAsync, tick } from '@angular/core/testing'
describe('testing setTimeout and setInterval:', () => {
it('setTimeout should work', fakeAsync(() => {
  let callback = jasmine.createSpy('setTimeoutCallback')
  setTimeout(() => {
    callback()
  }, 55)
  tick(56)
  expect(callback).toHaveBeenCalled();
}));
// let setInterval = setTimeout
it('setInterval should work', fakeAsync(() => {
  let callback = jasmine.createSpy('setTimeoutCallback')
  setInterval(() => {
    callback()
  }, 55)
  tick(56)
  expect(callback).toHaveBeenCalled();
}));
})

2 个答案:

答案 0 :(得分:0)

如果你将setInterval包装在服务中,你可以模拟服务,一切都是确定性的,没有任何副作用。基本上你只是做像

这样的事情
class IntervalService {

  setInterval(callback: () => void, interval: number) {
    return setInterval(callback, interval);
  }

  clearInterval(intervalId: number) {
    clearInterval(intervalId);
  }
}

你可以模仿它的方式是做类似的事情

class MockIntervalService {
  callback;

  setInterval(callback: () => any, interval: number) {
    this.callback = callback;
    return 1;
  }

  clearInterval(interval: number) {
    callback = undefined;
  }

  tick() {
    callback();
  }
}

然后在测试中,您可以通过在模拟服务上调用tick来控制间隔。最好的部分是一切都是同步的,使测试更容易推理。

注入服务需要额外的工作,但我不认为这是一个很大的负担。

答案 1 :(得分:0)

有关FakeAsync的一些理论,刷新:这将帮助您理解问题

通过使用fakeAsync,我们可以确保在测试中声明之前,所有异步代码都已运行,甚至可以更好地控制我们如何延长测试时间。

  • 当测试在fakeAsync区域中运行时,我们可以使用两个名为flushMicrotasks和tick的函数。 tick函数将时间提前指定的毫秒数,因此tick(100)将执行100ms内发生的所有异步任务。 flushMicrotasks函数将清除队列中当前存在的所有微任务。简而言之,如果您不确定时间或依赖于promise / settimeout,则需要使用flush()方法。

简化您的问题以了解答案:

it('should test some async code', fakeAsync(() => {
    let flag = false;
    setTimeout(() => { flag = true; }, 100);
    expect(flag).toBe(false); // PASSES
    tick(50);
    expect(flag).toBe(false); // PASSES
    tick(50);
    expect(flag).toBe(true); // PASSES
}));

其他可能的选择:

it('should test some asynccode', fakeAsync(() => {
    let flag = false;

    setTimeout(() => { flag = true; }, 100);

    expect(flag).toBe(false);

    flushMicrotasks();

    expect(flag).toBe(true); // FAILS
}));

在您的情况下,

flush()将起作用