Как мне издеваться над таймером RxJs 6?

Недавно мы обновили Angular 5 до Angular 6, а вместе с ним и RxJs 6. В рамках миграции использование таймера изменилось с:

Observable.timer()

to

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

В этом примере я пытаюсь запустить таймер, не дожидаясь разрешения таймера.


person MGDavies    schedule 08.06.2018    source источник
comment
Вместо этого используйте VirtualTimeScheduler. См. этот ответ. Или, что еще лучше, используйте мраморный тест.   -  person cartant    schedule 08.06.2018
comment
Спасибо за ответ Картант. Если я правильно понимаю, я могу использовать VirtualTimeScheduler для создания наблюдаемых таймеров в моей спецификации, но я пытаюсь смоделировать таймер, созданный в моем компоненте, и поэтому у меня нет прямого доступа к нему.   -  person MGDavies    schedule 08.06.2018
comment
Хорошо, если вы используете fakeAsync, вы можете положиться на то, что он имитирует setInterval — это то, что RxJS использует в своем AsyncScheduler — но вам нужно будет стереть метод now asyncScheduler, чтобы вернуть концепцию fakeAsync о текущем времени. См. этот ответ для механизма стирания. (Хотя async теперь называется asyncScheduler.) Разблокированный now будет называться Date.now() и это не будет работать с fakeAsync.   -  person cartant    schedule 09.06.2018


Ответы (1)


Поскольку вы используете fakeAsync, вы можете положиться на его исправление setInterval, чтобы имитировать реализацию наблюдаемого timer.

Однако вам нужно будет затереть метод now экземпляра asyncScheduler, так как он возвращает Date.now(). (Строго говоря, это не обязательно для наблюдаемого timer, поскольку вы его использовали, но это будет иметь значение для некоторых других наблюдаемых — например, наблюдаемого, возвращаемого оператором delay).

Вы можете заставить все работать довольно легко, если вы используете beforeEach и afterEach, чтобы стереть метод 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 для примера использования.

Реализация в основном такая же, как и в приведенном выше фрагменте, просто заключенная в функцию.


После написания этого ответа и добавления fakeSchedulers к rxjs-marbles я написал статью о Тестирование с поддельным временем.

person cartant    schedule 09.06.2018
comment
Спасибо, мне было концептуально сложно это понять, но вы мне очень помогли. Для тех, кто хочет дополнить данное объяснение и увидеть, как таймер опирается на asyncScheduler, я бы порекомендовал бегло взглянуть на источник: github.com/ReactiveX/rxjs/blob/ - person MGDavies; 11.06.2018