如何测试包含去抖操作符的observable?

Pet*_*ete 6 jasmine rxjs

如何编写Jasmine测试来测试debounce运算符的可观察量?我已经关注了这篇博文,了解了应该如何测试的原则,但它似乎没有用.

下面是我用来创建observable的工厂:

import Rx from "rx/dist/rx.all";
import DOMFactory from "../utils/dom-factory";
import usernameService from "./username.service";

function createUsernameComponent(config) {
  const element = DOMFactory(config);

  const username = Rx.Observable
    .fromEvent(element.find('input'), 'input')
    .pluck('target', 'value')
    .startWith(config.value);

  const isAvailable = username
    .debounce(500)
    .tap(() => console.info('I am never called!'))
    .flatMapLatest(usernameService.isAvailable)
    .startWith(false);

  const usernameStream = Rx.Observable.combineLatest(username, isAvailable)
    .map((results) => {
      const [username, isAvailable] = results;
      return isAvailable ? username : ''
    })
    .distinctUntilChanged();

  return Object.freeze({
    stream: usernameStream,
    view: element
  });
}

export default createUsernameComponent;
Run Code Online (Sandbox Code Playgroud)

请注意,tap测试员从不调用运算符.但是,如果我在浏览器上运行此代码,它将正确执行.

以下是我在测试中的尝试:

import Rx from "rx/dist/rx.all";
import Username from "./username.component";
import DataItemBuilder from "../../../test/js/utils/c+j-builders";
import usernameService from "./username.service"

describe('Username Component', () => {
  let input, username;

  beforeEach(() => {
    const usernameConfig = DataItemBuilder.withName('foo')
      .withPrompt('label').withType('text').build();

    const usernameComponent = Username(usernameConfig);
    usernameComponent.stream.subscribe(value => username = value);

    input = usernameComponent.view.find('input');
  });

  it('should set to a valid username after debounce', () => {
    const scheduler = injectTestSchedulerIntoDebounce();
    scheduler.scheduleRelative(null, 1000, () => {
      doKeyUpTest('abcddd', 'abcdd');
      scheduler.stop();
    });
    scheduler.start();
    scheduler.advanceTo(1000);
  });

  function injectTestSchedulerIntoDebounce() {
    const originalOperator = Rx.Observable.prototype.debounce;
    const scheduler = new Rx.TestScheduler();

    spyOn(Rx.Observable.prototype, 'debounce').and.callFake((dueTime) => {
      console.info('The mocked debounce is never called!');
      if (typeof dueTime === 'number') {
        return originalOperator.call(this, dueTime, scheduler);
      }
      return originalOperator.call(this, dueTime);
    });

    return scheduler;
  }

  function doKeyUpTest(inputValue, expectation) {
    input.val(inputValue);
    input.trigger('input');
    expect(username).toBe(expectation);
  }
});
Run Code Online (Sandbox Code Playgroud)

当我运行测试时,假的debounce永远不会被调用.username一旦我能够超越,我打算嘲笑这项服务debounce.

Sim*_*sch 1

在您的测试代码中,您将触发函数内的输入事件scheduleRelative。这不起作用,因为您在进行更改之前提前了 1000 毫秒。然后,去抖动器会等待 500 毫秒来对调用进行去抖动isAvailable,但您已经停止了调度程序,因此之后时间不会提前。

你应该做的是:在提前调度程序时间之前触发输入事件,或者在函数中触发输入事件scheduleRelative的时间<= 500ms,然后在函数内scheduleRelative触发1000ms,你必须使用expect预期的输出调用函数,然后停止调度程序。

它应该看起来像这样:

  it('should set to a valid username after debounce', () => {
    const scheduler = injectTestSchedulerIntoDebounce();

    scheduler.scheduleRelative(null, 500, () => {
      input.val(inputValue);
      input.trigger('input');
    });

    scheduler.scheduleRelative(null, 1000, () => {
      expect(username).toBe(expectation);
      scheduler.stop();
    });

    scheduler.start();
    scheduler.advanceTo(1000);
  });
Run Code Online (Sandbox Code Playgroud)

除此之外,我对scheduleAbsolute替代品有更好的经验scheduleRelative,因为它不那么令人困惑。