web-dev-qa-db-ja.com

RxJs 6タイマーをモックする方法は?

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

この例では、タイマーが解決するのを待たずにタイマーをトリガーしようとしています。

10
MGDavies

fakeAsyncを使用しているので、setIntervalのパッチ適用に依存して、timerオブザーバブルの実装を偽ることができます。

ただし、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に依存することは有用である可能性が高いため、fakeSchedulers関数を私の rxjs-marbles に追加しましたパッケージ。使用例については fake-spec.ts を参照してください。

実装は基本的に上記のスニペットと同じです-関数にラップされるだけです。


この回答を書いてfakeSchedulersrxjs-marblesに追加して以来、私は Testing with Fake Time に関する記事を書きました。

8
cartant