7
votes

I have a webapp project which uses rxjs5 to implement a flux and I am currently looking for solutions to write unit tests on it.

In fact, I have implemented custom observables inside, for example :

function getActivityObservable(events, timeout) {
  return Observable.create((observer) => {
    const deb = debounce(() => observer.next(false), timeout || DEFAULT_TIMEOUT);
    const sub = events.subscribe((e) => {
      if (!e) {
        deb.cancel();
        observer.next(false);
      } else {
        observer.next(true);
        deb(e);
      }
    });

    return () => {
      if (sub) sub.unsubscribe();
      if (deb) deb.cancel();
    };
  }).distinctUntilChanged();
}

I would like to test it using the marble testing way and write something like (i took a sample example from rxjs repository)

  describe("getActivityObservable", () => {
    it("should debounce by selector observable", () => {
      const e1 =   hot("--a--bc--d----|");
      const e1subs =   "^             !";
      const expected = "----a---c--d--|";

      expectObservable(e1.debounce(getTimerSelector(20))).toBe(expected);
      expectSubscriptions(e1.subscriptions).toBe(e1subs);
    });
  });

My question is:

Is it possible to use marble testing method (with operators like hot, cold and so on...) outside the rxjs5 project. I don't figure out how to use this nice tool in my project.

Thank you for your help.

1
You can, but it's not very ergonomic at the moment. Basically you need an instance of TestScheduler and it has all the createHotObservable and expectObservable methods and stuff on it. Then you call flush() on the scheduler to make it assert. - Ben Lesh
I see, Thanks. Would you recommend to use this method to test observables right now ? Or do you plan to expose marble testing in the future ? - Alexandre Duros
you could definitely use this method to tests observables right now, we are. It's just not very ergonomic to do so just yet. - Ben Lesh

1 Answers

3
votes

You can but as Ben comment: "it's not very ergonomic".

I'm using mocha and monkey patching it:

const isEqual = require('lodash.isequal');
const TestScheduler = require('rxjs/testing/TestScheduler').TestScheduler;

const assertDeepEqualFrame = (actual, expected) => {
  if (!isEqual(actual, expected)) {
    throw new Error('Frames not equal!');
  }
}

const oit = global.it;
global.it = function(description, cb, timeout) {
  if (cb.length === 0) {
    oit(description, function() {
      global.rxTestScheduler = new TestScheduler(assertDeepEqualFrame);
      cb();
      global.rxTestScheduler.flush();
    });
  } else { // async test
    oit.apply(this, arguments);
  }
};

I have taking a lot of inspirations from ngrx/store and especially this file: https://github.com/ngrx/store/blob/master/spec/helpers/test-helper.ts

and then can I write my test like:

it('should filter with an always-true predicate', () => {
  const source = hot('-1--2--^-3-4-5-6--7-8--9--|');
  const expected =          '--3-4-5-6--7-8--9--|';
  const predicate = () => { return true; };

  expectObservable(source.filter(predicate)).toBe(expected);
});

Edit You can see how I monkey patch it here: https://github.com/tjoskar/ng2-lazyload-image/blob/5e1c64a3611530ce26857a566b2d76dff890a3c5/test/helpers/test-helper.ts