在为RXJS observables编写测试时,如何避免通过我的业务逻辑传递调度程序?

时间:2015-10-22 14:15:18

标签: javascript unit-testing rxjs

我发现传递某些测试的唯一方法是将调度程序显式传递给函数。例如,请考虑此功能:

function doStuff( stream ){
    return stream.delay(100)
        .filter( x => x % 2 === 0 )
        .map( x => x * 2 )
        .flatMapLatest( x=> Rx.Observable.range( x, x+100) )

测试:

it('example test', () => {
    let scheduler = new Rx.TestScheduler()
    let xs = scheduler.createHotObservable(
        onNext(210, 1),
        onNext(220, 2),
        onNext(230, 3)
    )

    let res = scheduler.startScheduler(
        () => doStuff( xs, scheduler ),
        {created:0, subscribed:200, disposed:1000})

    expect( res.messages ).toEqual( [
        onNext(321, 4),
        onNext(322, 5),
        onNext(323, 6)
    ] )
})

哪个出错:

    Expected [  ] to equal [ ({ time: 321, value: OnNextNotification({ value: 4, kind: 'N' }), comparer: Function }), ({ time: 322, value: OnNextNotification({ value: 5, kind: 'N' }), comparer: Function }), ({ time: 323, value: OnNextNotification({ value: 6, kind: 'N' }), comparer: Function }) ].

这表明delay发生在真实的时间而不是TestScheduler的模拟时间。

如果我将调度程序传递给每个操作符,那么我可以使它工作:

function doStuff( stream, scheduler ){
   return stream.delay( 100, scheduler )
      .filter( x => x % 2 === 0 )
      .map( x => x * 2 )
      .flatMapLatest( x => Rx.Observable.range(x, 3, scheduler ) )
}

但我觉得我应该能够设置一次调度程序而不必让我的实际生产代码必须通过它。我真的很期待,因为原始流是从TestScheduler创建的,然后通过相同的调度程序运行,所以这一切都将自动连接起来。

1 个答案:

答案 0 :(得分:2)

RX指南建议consider passing a specific scheduler to concurrency introducing operators。对于单线程Javascript,没有并发性,但基于时间的运算符如delay()也有类似的问题。

这并不像我初想的那么糟糕,因为大多数运营商没有调度程序参数,只有其中一部分是基于时间的。这突出了您明确传递调度程序的原因。在上面的例子中,我通过调度程序向每个支持它的操作员传递,但结果并不完全符合我的预期 - 我甚至调整了我的“预期”结果以使其工作:

expect( res.messages ).toEqual( [
    onNext(321, 4),
    onNext(322, 5),
    onNext(323, 6)
] )

但实际上,我期待所有这些时间都是320

对于delay(),我需要从测试中继承调度程序,但对于range()我想要默认的调度程序,因为它会立即安排事件。

我的最终示例代码段将如下所示:

function doStuff( stream, scheduler ){
    return stream.delay( 100, scheduler )
        .filter( x => x % 2 === 0 )
        .map( x => x * 2 )
        .flatMapLatest( x => Rx.Observable.range(x, 3))
}

创建测试时,总是希望将TestScheduler用于基于时间的运算符。无论测试是否正在运行,其他运营商都可能希望使用DefaultScheduler。