Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/28.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何使用jasmine测试使用setInterval的angular服务?_Javascript_Angular_Timer_Jasmine - Fatal编程技术网

Javascript 如何使用jasmine测试使用setInterval的angular服务?

Javascript 如何使用jasmine测试使用setInterval的angular服务?,javascript,angular,timer,jasmine,Javascript,Angular,Timer,Jasmine,我正在尝试设置setInterval调用的测试。setInterval始终会创建一个新计时器,因此无论您向前移动多远,tick()始终会出现以下错误消息: 错误:队列中仍有1个计时器 如果我用setInterval覆盖setTimeout函数,那么测试将按预期通过。我使用公共属性myTimerFunc(通常设置为setInterval)添加了代码,以允许我的angular服务使用该属性。测试代码将其设置为setTimeout 有没有更好的方法来测试setInterval? 如果jasmine可以

我正在尝试设置setInterval调用的测试。setInterval始终会创建一个新计时器,因此无论您向前移动多远,tick()始终会出现以下错误消息:

错误:队列中仍有1个计时器

如果我用setInterval覆盖setTimeout函数,那么测试将按预期通过。我使用公共属性myTimerFunc(通常设置为setInterval)添加了代码,以允许我的angular服务使用该属性。测试代码将其设置为setTimeout

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

如果jasmine可以忽略队列中的最后一个计时器,那就太好了。这样我就可以不用额外的拐杖来测试了

例如,除非取消对覆盖setInterval的行的注释,否则此代码将失败。我不介意在测试代码中这样做,但我确实希望避免在普通代码中添加额外的内容来允许这种覆盖

import{fakeAsync,tick}来自“@angular/core/testing”
描述('测试设置超时和设置间隔:',()=>{
它('setTimeout应该工作',fakeAsync(()=>{
让callback=jasmine.createSpy('setTimeoutCallback')
设置超时(()=>{
回调函数()
}, 55)
勾选(56)
expect(callback).toHaveBeenCalled();
}));
//设setInterval=setTimeout
它('setInterval应该工作',fakeAsync(()=>{
让callback=jasmine.createSpy('setTimeoutCallback')
设置间隔(()=>{
回调函数()
}, 55)
勾选(56)
expect(callback).toHaveBeenCalled();
}));

})
如果将
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();
  }
}
你可以通过这样的方式来嘲笑它

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();
  }
}
然后在测试中,您可以通过调用模拟服务上的
勾选
来控制间隔。最好的一点是,一切都是同步的,这使得测试更容易推理


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

一些关于FakeAsync的理论,flush:,这将帮助您理解问题

通过使用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()可以用了

谢谢。我同意你的建议不是很大的负担,特别是因为我正在考虑限制setInterval的重复次数(而不是让它离开服务器)