Angular 2 fakeAsync在使用tick()的函数中等待超时?

时间:2017-03-28 04:56:09

标签: javascript unit-testing angular asynchronous jasmine

我试图从Angular 2中的模拟后端获得单元测试的结果。目前,我们使用fakeAsync超时来模拟时间的流逝。

当前工作单位测试

it('timeout (fakeAsync/tick)', fakeAsync(() => {
    counter.getTimeout();
    tick(3000); //manually specify the waiting time
}));

但是,这意味着我们仅限于手动定义的超时。不是在异步任务完成时。我要做的是让tick()等到任务完成后再继续测试。

这似乎不按预期工作。

阅读fakeAsynctick答案here解释说:

  

tick()模拟异步时间的流逝。

我设置了plnkr example来模拟这种情况。

在这里,我们调用getTimeout()方法,该方法调用具有超时的内部异步任务。在测试中,我们尝试将其包装并在调用tick()方法后调用getTimeout()

counter.ts

getTimeout() {
  setTimeout(() => {
    console.log('timeout')
  },3000)
}

counter.specs.ts

it('timeout (fakeAsync/tick)', fakeAsync(() => {
    counter.getTimeout();
    tick();
}));

但是,单元测试失败并出现错误"错误:1个计时器仍在队列中。"

issue here in the angular repo是否与此有关?

是否可以使用tick()这种方式等待超时功能?或者我可以使用另一种方法吗?

7 个答案:

答案 0 :(得分:15)

尝试在测试结束时添加以下功能调用之一或组合:

    flush();
    flushMicrotasks();
    discardPeriodicTasks();
  1. flush(带有可选的maxTurns参数)也会刷新宏任务。 (Angular测试教程中未提及此功能。)
  2. flushMicrotasks刷新微任务队列。
  3. discardPeriodicTasks取消“周期性定时器仍在队列中”。

队列中的计时器不一定表示您的代码有问题。例如,观察当前时间的组件可能会引入此类计时器。如果您使用外部库中的此类组件,则可能还会考虑对它们进行存根而不是“追赶计时器”。

为进一步理解,您可以查看fakeAsynczone-testing.js函数的javascript代码。

答案 1 :(得分:2)

在每个测试的末尾添加:

 fixture.destroy();
 flush();

答案 2 :(得分:1)

试试这个:

// I had to do this:
it('timeout (fakeAsync/tick)', (done) => {
  fixture.whenStable().then(() => {
       counter.getTimeout();
       tick();
    done();
  });
});

Source

答案 3 :(得分:0)

我通常在单元测试中使用flushMicrotasks方法与我的服务一起使用。我读过tick()与flushMicrotasks非常相似,但也调用了jasmine tick()方法。

答案 4 :(得分:0)

fakeAsync的目的是在您的规范内控制时间。 tick不会等待任何时间,因为它是用于模拟时间流逝的同步功能。如果要等到异步功能完成,则需要使用asyncwhenStable,但是,在您的示例中,规范需要3秒钟才能通过,因此我不会建议这个。

counter.spec.ts 失败的原因是您仅模拟了0秒的经过(通常用于表示事件循环的下一个滴答声)。因此,当规范完成时,仍然有模拟计时器处于活动状态,这会使整个规范失效。通知您模拟了超时,并且未处理,它实际上在正常工作。

基本上,我认为您正在尝试以不打算使用的方式使用fakeAsynctick。如果您需要按照建议的方法测试超时,最简单的方法就是自己模拟setTimeout函数,以便无论使用什么时间,都可以调用该方法。

已编辑 我遇到了一个相关的问题,我想清除计时器,由于它不是要测试的部分,所以我不在乎它花了多长时间。我尝试过:

tick(Infinity);

曾工作过,但超级hacky。我最终选择了

discardPeriodicTasks();

我的所有计时器都清除了。

答案 5 :(得分:0)

对我来说,上述所有操作均无济于事,但在测试代码中两次调用tick(<async_time>)

到目前为止,我的解释是:对于每个异步调用,您都需要一个/自己的tick()调用。

此后我有一个.pipe(debounceTime(500))和一个timer(500).subscribe(..),这有所帮助:

tick(500);
tick(500);

答案 6 :(得分:-1)

异步

test.service.ts

export class TestService {
  getTimeout() {
    setTimeout(() => { console.log("test") }, 3000);
  }
}

test.service.spec.ts

import { TestBed, async } from '@angular/core/testing';

describe("TestService", () => {
  let service: TestService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [TestService],
    });

    service = TestBed.get(TestService);
  });

  it("timeout test", async(() => {
    service.getTimeout();
  });
});

伪异步

test.service.ts

export class TestService {
  readonly WAIT_TIME = 3000;

  getTimeout() {
    setTimeout(() => { console.log("test") }, this.WAIT_TIME);
  }
}

test.service.spec.ts

import { TestBed, fakeAsync } from '@angular/core/testing';

describe("TestService", () => {
  let service: TestService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [TestService],
    });

    service = TestBed.get(TestService);
  });

  it("timeout test", fakeAsync(() => {
    service.getTimeout();
    tick(service.WAIT_TIME + 10);
  });
});