用Jest模拟delay()RxJS

时间:2019-04-08 08:09:47

标签: javascript unit-testing rxjs jestjs

是否有一种简单的方法来模拟RxJS的delay()方法,例如在虚假的时间内进行观察?

我有这种方法:

register(user) {
  return this._checkLog(user).delay(500).flatMap( ... )
}

当我删除delay()方法时,我从_register()进行的测试全部成功。

4 个答案:

答案 0 :(得分:2)

RxJS v6

对于RxJS v6这样的代码:

code.js

import { of } from 'rxjs';
import { delay } from 'rxjs/operators';

export const example = of('hello').pipe(
  delay(1000)
);

...您可以像这样使用sinon fake timers

import * as sinon from 'sinon';
import { example } from './code';

describe('delay', () => {
  let clock;
  beforeEach(() => { clock = sinon.useFakeTimers(); });
  afterEach(() => { clock.restore(); });

  it('should delay one second', () => {
    const spy = jest.fn();
    example.subscribe(spy);

    expect(spy).not.toHaveBeenCalled();  // Success!
    clock.tick(1000);
    expect(spy).toHaveBeenCalledWith('hello');  // Success!
  });
});

(请注意,在撰写Jest timer mocks时不起作用,不确定为什么)


...或者您可以嘲笑delay来执行以下操作:

import { delay } from 'rxjs/operators';
import { example } from './code';

jest.mock('rxjs/operators', () => {
  const operators = jest.requireActual('rxjs/operators');
  operators.delay = jest.fn(() => (s) => s);  // <= mock delay
  return operators;
});

describe('delay', () => {
  it('should delay one second', () => {
    const spy = jest.fn();
    example.subscribe(spy);

    expect(delay).toHaveBeenCalledWith(1000);  // Success!
    expect(spy).toHaveBeenCalledWith('hello');  // Success!
  });
});

RxJS v5

对于RxJS v5这样的代码:

code.js

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/delay';

export const example = Observable.of('hello').delay(1000);

...您可以嘲笑delay来执行以下操作:

import { Observable } from 'rxjs/Observable';
import { example } from './code';

jest.mock('rxjs/add/operator/delay', () => {
  const Observable = require('rxjs/Observable').Observable;
  Observable.prototype.delay = jest.fn(function () { return this; });  // <= mock delay
});

describe('delay', () => {
  it('should delay one second', () => {
    const spy = jest.fn();
    example.subscribe(spy);

    expect(Observable.prototype.delay).toHaveBeenCalledWith(1000);  // Success!
    expect(spy).toHaveBeenCalledWith('hello');  // Success!
  });
});

答案 1 :(得分:1)

从 6.2.1 版本开始,RxJS 支持 Jest 的假时间,所以你可以简单地写

jest.useFakeTimers('modern');

test('...', () => {
  // ...code that subscribes to your observable.

  jest.runAllTimers();

  // ...code that makes assertions.
});

此外,我发布了一个 library,它使编写此类测试更容易,这是一个示例(log 将日志记录添加到 observable,getMessages 检索已记录的消息):

import { getMessages, log } from '1log';
import { of } from 'rxjs';
import { delay } from 'rxjs/operators';

test('delay', () => {
  of(42).pipe(delay(500), log).subscribe();
  jest.runAllTimers();
  expect(getMessages()).toMatchInlineSnapshot(`
    [create 1] +0ms [Observable]
    [create 1] [subscribe 1] +0ms [Subscriber]
    [create 1] [subscribe 1] [next] +500ms 42
    [create 1] [subscribe 1] [complete] +0ms
    · [create 1] [subscribe 1] [unsubscribe] +0ms
  `);
});

答案 2 :(得分:0)

我们正在使用Rxjs中的Scheduler s。

类看起来像这样:

import { Observable, Scheduler, Subject, asapScheduler } from 'rxjs';

// ...

constructor(
    @Optional() private readonly _scheduler: Scheduler
) {
    if (isNullOrUndefined(_scheduler)) {
        this._scheduler = asapScheduler;
    }
}

// ...

this._someObservable.pipe(delay(1, this._scheduler));

然后,在规格文件中的TestModuleMetadata中提供模拟:

{
    declarations: [YourComponent],
    imports: [],
    providers: [
        { provide: Scheduler, useValue: new VirtualTimeScheduler() },
    ],
};

现在,您需要做的就是在beforeEach块中分配调度程序,并在希望“跳过”延迟时刷新它:

let schedulerMock = Testbed.get(Scheduler);

// ...

it('should emit true', () => {
    let result: boolean = null;
    comp.someObservable.subscribe(next => (result = next));
    schedulerMock.flush();

    expect(result).toBe(true);
});

这也适用于其他时间相关的运算符,例如bufferTime。 您要使用或需要在组件中使用哪个调度程序应取决于您的用例,在最佳情况下,请查阅文档并找出最适合您的方案。

答案 3 :(得分:0)

要完成RxJS 6的brian-live-outdoor解决方案,您还可以使用delayWhen和与jest配合使用的计时器来模拟delay()的实际行为:

 jest.mock("rxjs/operators", () => {
  const operators = jest.requireActual("rxjs/operators");
  const observables = jest.requireActual("rxjs");
  operators.delay = jest.fn(delay => s =>
    s.pipe(operators.delayWhen(() => observables.timer(delay)))
  );
  return operators;
});

,您可以将此模拟放到node_modules文件夹旁边:

.
├── __mocks__
│   └── rxjs
│       └── operators.js
└── node_modules
// operators.js

const operators = require("rxjs/operators");
const observables = require("rxjs");

operators.delay = jest.fn(delay => s =>
  s.pipe(operators.delayWhen(() => observables.timer(delay)))
);

module.exports = operators;

一个以前无法使用且无法与该模拟一起使用的测试示例:

it("some test with delay()", (done: DoneFn) => {

    let check = false;

    jest.useFakeTimers();

    of(true)
      .pipe(delay(1000))
      .subscribe(() => (check = true));

    setTimeout(() => {
      expect(check).toBe(true);
      done();
    }, 2000);

    jest.runTimersToTime(999);
    expect(check).toBe(false);

    jest.runAllTimers();
  });