How do I test a function that returns an observable using timed intervals in rxjs 5?
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 theTestScheduler
.The
TestScheduler
needs to get a method to deep compare objects. It'll be used to compare arrays ofNotification
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 so50
we're using in this test isn't the same as50ms
. We just need to fit inside that750
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.
You create either a TestScheduler
or VirtualTimeScheduler
and pass it as the optional second argument to Observable.Interval
. In your case you'd simply add an optional scheduler argument to timeRange
and pass the value on.
These test schedulers allow you to "pass time" without waiting for actual time to elapse.
For more information and examples, see Testing your Rx Application
I faced the same problem recently and discovered marble testing which lets you write awesome visual tests that make timed Observable testing extremely easy.
Marble testing abstracts time away and the tests run instantly (by using TestScheduler under the hood), but you still can test complex timed processes. This is an example test to give you an idea of the syntax:
it('should set up an interval', () => {
const expected = '----------0---------1---------2---------3---------4---------5---------6-----';
expectObservable(Observable.interval(100, rxTestScheduler)).toBe(expected, [0, 1, 2, 3, 4, 5, 6]);
});
This actually is the official unit test from for Observable.interval
from the rxjs5 repo: https://github.com/ReactiveX/rxjs/blob/master/spec/observables/interval-spec.ts
Check out the official documentation for more info.
Getting it running was a bit of a challenge, but all you basically need are the two files marble-testing.ts
and test-helper.ts
from this marble testing example project.