2
votes

I have a function that takes an array and returns an Observable (essentially emits each value from the given array after each delay):

export const typewriter = <T>(str: T[], delay = 20) =>
  zip(interval(delay), from(str)).pipe(map(([, str]) => str));

I want to write unit tests for this, and I'm trying to use rxjs-marbles and follow the writing marble tests instructions from RxJS.

All of the examples seem to require a hot source observable and a cold observable for you to compare expected values. In this case I have a function that returns a cold observable. I've tried writing it like this:

const expected = m.hot('-^-a-b-(c|)');

const source = typewriter(['a', 'b', 'c']);

m.expect(source).toBeObservable(expected);

However I always get Expected <blank> to equal ..., i.e. the source is blank. I think that this is because the source Observable is not hot, but even if I do source.subscribe() before the assertion I get the same error. Using m.cold for the test observable doesn't matter.

How can I test a function that returns an observable using RxJS marbles?

1
I think the (or at least one) issue is that interval uses the async scheduler by default. You need to pass the TestScheduler to it, meaning you need to extend your typewriter to take an (optional) scheduler argument which it forwards to from and interval. For from it's not needed to make it work (I believe), but since you need to add it anyway, might as well do it right :-)Ingo Bürk
That said, using a cold observable for the expected state should be totally fine then.Ingo Bürk

1 Answers

5
votes

Your typewriter function uses the interval observable creator, but passes only the delay. That means it will be using the creator's default scheduler.

To be used with marble tests, it needs to use the TestScheduler - available via m.scheduler.

Passing a test scheduler to a deeply-nested observable can be a pain. rxjs-marbles includes a bind method - see the docs - to make this a little easier:

m.bind();
const expected = m.hot('-^-a-b-(c|)');
const source = typewriter(['a', 'b', 'c']);
m.expect(source).toBeObservable(expected);

Calling bind monkey patches all of the schedulers to forward calls to the test's TestScheduler.