11

I brought the book "rxjs in action" and just finish the testing section.

Testing rxjs codes are different then usual testing, because everything are lazy loading.

In the book, they mention two test method, either passing done(I am using QUnit and done signals async code is finish) or marble diagrams.

My question is, which method should I choose, that I have mentioned above?

Liam
  • 27,717
  • 28
  • 128
  • 190
softshipper
  • 32,463
  • 51
  • 192
  • 400
  • Could you be more specific? What are marble diagrams tests? What aspects do you need to test in your specific situation? Doesn't the book contrast the pros and cons of both types of tests? – AngularChef Jan 21 '17 at 15:38
  • marble diagrams [link](https://github.com/ReactiveX/rxjs/blob/master/doc/writing-marble-tests.md). The book does not cover pros and cons, just in which way rxjs could be tested. – softshipper Jan 21 '17 at 16:25
  • Interesting, I didn't know about those. – AngularChef Jan 21 '17 at 18:44

2 Answers2

12

I have been getting this question a lot from my colleagues. I finally got around to document my ways of testing RxJs on my blog. Since your question seems to be related to RxJs5 i will only quote the relevant part of my post here.

Testing in RxJs5 the RxJs4 way

When you migrate your codebase from RxJs4 towards 5 you will find out that a lot of things have been moved, renamed and above all that the implementation of the TestScheduler is no longer available. RxJs contributor kwonoj has created a compatibility shim to help migration towards RxJs5. You can install it using npm npm install @kwonoj/rxjs-testscheduler-compat. Not all features of the TestScheduler are implemented but the most important .startScheduler is working.

const TestScheduler = require('@kwonoj/rxjs-testscheduler-compat').TestScheduler;
const next = require('@kwonoj/rxjs-testscheduler-compat').next;
const complete = require('@kwonoj/rxjs-testscheduler-compat').complete;

it('works in RxJs5 with the compat package', () => {
  const scheduler = new TestScheduler(); // Note; no longer the Rx.TestScheduler

  const results = scheduler.startScheduler(
    () => Rx.Observable.interval(100, scheduler).take(3),
    { created: 100, subscribed: 200, unsubscribed: 1000 } // NOTE: disposed is now renamed to unsubscribed
  );

  collectionAssert.assertEqual(res.messages, [
    next(200 + 100, 0),
    next(200 + 200, 1),
    next(200 + 300, 2),
    complete(200 + 300)
  ]);
});

Testing in RxJs5 using the new Marble testing syntax

The RxJs team has introduced marble testing syntax to more visually define how your operator or custom code should operate.

var e1 = hot('----a--^--b-------c--|');
var e2 = hot(  '---d-^--e---------f-----|');
var expected =      '---(be)----c-f-----|';

expectObservable(e1.merge(e2)).toBe(expected);

At the time of writing this post they have not yet made this approach really easy to use outside of the RxJs5 library itself. There are implementations available to see how to do it yourself. You can also look around in the codebase of RxJs5 to see how to setup your testing framework to do your own marble tests. There is an open issue about documenting testing with RxJs5. I have not yet succeeded to get my testing framework setup to do marble testing in this way.

Mark van Straten
  • 9,287
  • 3
  • 38
  • 57
  • 1
    It sounds like, that rxjs5 is not testable yet. – softshipper Jan 23 '17 at 07:09
  • Good summary of the current state of testing with RxJS 5. Thanks for the link to the shim - in my view this is the only really viable option at the moment! So to respond to @zero_coding - with this, RxJS 5 is fully testable in the same way as every other Rx port, but not without. – LJW Feb 10 '17 at 21:47
  • 3
    @zero_coding agree. My impression is that RxJS is becoming a victim of its own implementation complexity, when it comes to unit testing. Testing promises is so straightforward, same for async/await, just a simple .then(done). But observables are abstraction over pretty much everything... which makes unit testing so blurry... – ducin Sep 07 '17 at 20:40
  • This information is out of date – Liam Dec 08 '21 at 14:53
12

Time has passed, and now it is definitely possible (even easy) to use these marble tests yourself using TestScheduler. They are a great way to comprehensively test emissions over time, errors, completions and subscriptions in an easy-to-understand format. Here is a sample from their docs:

import { TestScheduler } from 'rxjs/testing';

const testScheduler = new TestScheduler((actual, expected) => {
  // asserting the two objects are equal
  // e.g. using chai.
  expect(actual).deep.equal(expected);
});

// This test will actually run *synchronously*
it('generate the stream correctly', () => {
  testScheduler.run(helpers => {
    const { cold, expectObservable, expectSubscriptions } = helpers;
    const e1 =  cold('-a--b--c---|');
    const subs =     '^----------!';
    const expected = '-a-----c---|';

    expectObservable(e1.pipe(throttleTime(3, testScheduler))).toBe(expected);
    expectSubscriptions(e1.subscriptions).toBe(subs);
  });
});

If you use Jasmine, I wrote a little helper called marbleTest() to reduce boilerplate, available in @s-libs/ng-dev:

import { marbleTest } from "s-ng-dev-utils";

it("generate the stream correctly", marbleTest(helpers => {
  const { cold, expectObservable, expectSubscriptions, testScheduler } = helpers;
  const e1 = cold(" -a--b--c---|");
  const subs = "    ^----------!";
  const expected = "-a-----c---|";

  expectObservable(e1.pipe(throttleTime(3, testScheduler))).toBe(expected);
  expectSubscriptions(e1.subscriptions).toBe(subs);
}));
Eric Simonton
  • 5,702
  • 2
  • 37
  • 54