【问题标题】:How do I mock RxJs 6 timer?如何模拟 RxJs 6 计时器?
【发布时间】:2018-11-18 10:41:40
【问题描述】:

我们最近从 Angular 5 更新到了 Angular 6,以及 RxJs 6。 作为迁移的一部分,计时器的使用已从:

Observable.timer()

timer()

在我们的测试中有很多地方我们使用以下模式模拟计时器可观察对象。

let timerObserver: Observer<any>;

 beforeEach(() => {
 spyOn(Observable, 'timer').and.returnValue(Observable.create(
    ((observer: Observer<any>) => {
      timerObserver  = observer;
    })
  ));
});

it(`should not have any notifications by default`, () => {
   timerObserver.next('');
   ...
});

有人知道如何迁移这种模式吗?


编辑:我在这里创建了问题的简化说明:

https://stackblitz.com/edit/angular-v6-testing-template-nm7add

// Hello.Component
      ngOnInit() {
        const timer$ = timer(30);
        timer$.subscribe(() => {
          this.testMe = 'this has been changed';
        });
      }

// Hello.component.spec
  it('should set testMe after a given timer', fakeAsync(() => {
    tick(50);
    expect(fixture.componentInstance.testMe).toBe('this has been changed');
  }));

在这个例子中,我试图让计时器触发而不等待计时器解决。

【问题讨论】:

  • 请改用VirtualTimeScheduler。见this answer。或者——甚至更好——使用marble test
  • 感谢您的回复卡坦特。如果我理解正确,我可以使用 VirtualTimeScheduler 在我的规范中创建计时器可观察对象,但我想做的是模拟在我的组件中创建的计时器,因此我无法直接访问它。
  • 好的,如果你使用fakeAsync,你可以依赖它来模拟setInterval——这是RxJS在它的AsyncScheduler中使用的——但是你需要破坏asyncSchedulernow 方法返回fakeAsync 的当前时间概念。有关破坏机制,请参阅this answer。 (虽然,async 现在被命名为 asyncScheduler。)未破坏的,now 将被称为 Date.now()that won't workfakeAsync

标签: angular jasmine rxjs6


【解决方案1】:

当您使用fakeAsync 时,您可以依靠它对setInterval 的修补来伪造timer observable 的实现。

但是,您需要破坏asyncScheduler 实例的now 方法,因为它返回Date.now()。 (严格来说,这对于 timer 可观察对象来说不是必需的,因为您已经使用了它,但它对于其他一些可观察对象很重要 - 例如 delay 运算符返回的可观察对象)。

如果你使用beforeEachafterEach 来破坏now 方法并配置一个跟踪虚假时间的函数,你可以很容易地让事情工作:

import { fakeAsync, tick as _tick } from '@angular/core/testing';
import { asyncScheduler, of, timer } from 'rxjs';
import { delay } from 'rxjs/operators';

describe('fakeAsync and RxJS', () => {

  let tick: (milliseconds: number) => void;

  beforeEach(() => {
    let fakeNow = 0;
    tick = milliseconds => {
      fakeNow += milliseconds;
      _tick(milliseconds);
    };
    asyncScheduler.now = () => fakeNow;
  });

  it('should support timer with fakeAsync', fakeAsync(() => {
    const source = timer(100);
    let received: number | undefined;
    source.subscribe(value => received = value);
    tick(50);
    expect(received).not.toBeDefined();
    tick(50);
    expect(received).toBe(0);
  }));

  it('should support delay with fakeAsync', fakeAsync(() => {
    const source = of(0).pipe(delay(100));
    let received: number | undefined;
    source.subscribe(value => received = value);
    tick(50);
    expect(received).not.toBeDefined();
    tick(50);
    expect(received).toBe(0);
  }));

  afterEach(() => {
    delete asyncScheduler.now;
  });
});

实际上,因为依靠fakeAsync 来模拟基于时间的可观察对象可能很有用,所以我在rxjs-marbles 包中添加了一个fakeSchedulers 函数。用法见fake-spec.ts

实现与上面sn-p中的基本相同——只是封装在一个函数中。


自从写下这个答案并将fakeSchedulers添加到rxjs-marbles之后,我写了一篇关于Testing with Fake Time的文章。

【讨论】:

  • 谢谢,这在概念上对我来说很难理解,但你帮了我很多。对于那些有兴趣补充给定解释并了解计时器如何依赖 asyncScheduler 的人,我建议快速查看源代码:github.com/ReactiveX/rxjs/blob/…
猜你喜欢
  • 2018-10-18
  • 1970-01-01
  • 1970-01-01
  • 2019-10-04
  • 1970-01-01
  • 2010-12-11
  • 2014-12-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多