Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The right way to test rxjs

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?

like image 845
softshipper Avatar asked Jan 21 '17 13:01

softshipper


People also ask

Which kind of software test is used in RxJS?

You'll learn to use RxJS's observable-based testing to make asynchronous testing easier. With the help of a JavaScript testing framework, Mocha. js, as well as an RxJS instrumentation tool known as a virtual scheduler, you can learn to test streams that compose any sort of asynchronous code easily.

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 toBeObservable?

toBeObservable acts synchronously and waits for all the code inside the observable to be executed. The same code in jest and jest-marbles will always fail with the following message: Expected mock function to have been called, but it was not called. Because the last assertion is evaluated before the observable.


2 Answers

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.

like image 120
Mark van Straten Avatar answered Sep 28 '22 01:09

Mark van Straten


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);
}));
like image 22
Eric Simonton Avatar answered Sep 28 '22 03:09

Eric Simonton