Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I test a function that returns an observable using timed intervals in rxjs 5?

Tags:

rxjs

rxjs5

For example, I have a function like this:

export function timeRange(start: number, end: number): Rx.Observable<number> {
  return Rx.Observable.interval(1000)
  .map(n => n + start)
  .take(end - start + 1)
}

And I had a unit test that ran timeRange(10, 100) and asserted values against it.

The problem is the time intervals would make my test too long to run.

How would you reduce the time interval without touching the function itself?

I tried reading the docs on schedulers but I didn't get it.

like image 557
khoomeister Avatar asked Mar 11 '17 08:03

khoomeister


People also ask

What is marble testing?

What is marble testing? Marble testing allows you to test asynchronous RxJS code synchronously and step-by-step with the help of RxJS TestScheduler test utility and using virtual time steps. There are also marble diagrams which demonstrate what is happening with a particular operation in the observable stream.

What is Take operator in RxJS?

take returns an Observable that emits only the first count values emitted by the source Observable. If the source emits fewer than count values then all of its values are emitted. After that, it completes, regardless if the source completes.


1 Answers

There're a couple of caveats but you can test your function as follows:

const Rx = require('rxjs');
const chai = require('chai');

function timeRange(start, end, interval = 1000, scheduler = Rx.Scheduler.async) {
  return Rx.Observable.interval(interval, scheduler)
    .map(n => n + start)
    .take(end - start + 1)
}

let scheduler = new Rx.TestScheduler(chai.assert.deepEqual);
let source = timeRange(2, 8, 50, scheduler);
let values = {'2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8};
scheduler.expectObservable(source).toBe('-----2----3----4----5----6----7----(8|)', values);

scheduler.flush();

A few points to notice:

  • The function takes four arguments now. The interval and the Scheduler that we need to use to pass the TestScheduler.

  • The TestScheduler needs to get a method to deep compare objects. It'll be used to compare arrays of Notification objects.

  • You can't use 1000 because maximum time allowed is hardcoded as 750. Note, that this is just virtual time and is unrelated to real time so 50 we're using in this test isn't the same as 50ms. We just need to fit inside that 750 time frame.

  • Then marble tests as typical marble test. See https://github.com/ReactiveX/rxjs/blob/master/doc/writing-marble-tests.md for more information. Just be aware that I can't use the same global mocha functions as the default RxJS operators.

  • I had to specify also the values because marble tests by default use strings and I need to force integers to ensure deep equality.

You can test that this fails by changing one marble value:

let values = {'2': 42, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8};

Also, you can see what the notification objects are by printing them to console:

let scheduler = new TestScheduler((actual, expected) => {
   console.log('Actual:', actual, '\n\n', 'Expected:', expected);
   chai.assert.deepEqual(actual, expected);
});

Using RxJS 5 mocha helpers

At this moment RxJS 5 doesn't expose the mocha testing helpers. I'm actually using it in another project I'm working on right now (including generating diagram images). You can have a look here https://github.com/martinsik/rxjs-extra into package.json what scripts I'm using. It works well but the setup is a little hard to understand. It involves downloading the RxJS 5 archive, copying a few files and patching it with a couple of files. Then the default options for mocha test are set with mocha.opts that is based on the original from RxJS 5.

like image 197
martin Avatar answered Oct 16 '22 08:10

martin