Explosion Pills
Explosion Pills

Reputation: 191729

Using marble testing for a function that returns an Observable

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?

Upvotes: 2

Views: 1969

Answers (1)

cartant
cartant

Reputation: 58400

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.

Upvotes: 6

Related Questions