I have a function that could be considered long running (actually, it's multi-step where each step could be waiting for an external event like a response from a HTTP call).
There's an invoker function to this which should return an observable that returns the updates to the original function. The original function MUST run whether or not the returned observable is subscribed to. Basically, it needs to return a hot observable.
I have tried the following approach but, cannot get it to work:
function longRunningOperation(): Observable<number> {
const updates$ = new Subject<number>();
Promise.resolve().then(() => {
console.log('starting updates...');
updates$.next(1);
updates$.next(2);
updates$.next(3);
updates$.complete();
});
return updates$;
}
If I do a marble test on the above, I see that the actual events generated being empty (though the function does execute).
it('should return and start executing', () => {
const updates$ = longRunningOperation();
const marbles = '(abc|)';
const events = { a: 1, b: 2, c: 3 };
new TestScheduler((actual, expected) =>
expect(actual).toEqual(expected)
).run(({ expectObservable }) => {
expectObservable(updates$).toBe(marbles, events);
console.log('Test Scheduler subscribed');
});
});
What am I doing wrong here?
Link to demo https://stackblitz.com/edit/jasmine-in-angular-upoavr?file=src/app/app.component.spec.ts
I do not think you can test this scenario with marbles, at least in a simple way.
Marbles are a synchronous mechanism, while
Promise
s are always asynchronous.Therefore, when your test executes the
run
method ofTestScheduler
it does it synchronously. ThePromise
though will be resolved later by the JS engine, so only later theupdate$
Subject will emit its values.This is the reason why the test says that it gets 0 notifications from
update$
rather than the 4 it expects. The notifications fromupdate$
will come after the assertion has been evaluated.If you want to test this scenario without marbles, you can do something like this
as can be seen in this stackblitz.
UPDATE
Maybe there is a way to test your scenario with marbles, but this requires a change in the structure of your function.
Marble tests, for what is my experience, define some sort of source streams, apply to such streams some kind of transformations and then compare the results of the transformation with the expected stream values.
In this case the source stream can be a simple Observable that notifies just once. This notification than triggers the execution of the long running function which itself causes
update$
to notify.So, we can change slightly the
longRunningOperation
function like thisOnce you have done so, you have created the space for a source stream (in this case represented by the Observable passed to the
longRunningOperation_
function as parameter) and so the test can be run like thisLook at this stackblitz for the full example.