Akxe
Akxe

Reputation: 11475

Expect to throws actually crashes when used with RXJS catchError

it('minimal test case', () => {
    expect(() => {
        of(1).pipe(
            map(() => {
                throw new Error('err');
            }),
            catchError(() => {
                throw new Error('new err');
            }),
        ).subscribe();
    }).toThrow();
});

This code will actually crash the whole execution of Jasmine/Karma, even in the original Error stacktrace leads to the Error inside catchError.

I think that if observable throws and cannot handle it should propagate that error in the context it is in. Otherwise, I cannot test if the observable throws.

This (Jasmine) test will produce following errors:

Uncaught Error: new err at CatchSubscriber.selector (slideshow.directive.spec.ts:224) at CatchSubscriber.push.../../node_modules/rxjs/_esm5/internal/operators/catchError.js.CatchSubscriber.error (catchError.js:34) at MapSubscriber.push.../../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next (map.js:38) at MapSubscriber.push.../../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:53) at Observable._subscribe (subscribeToArray.js:5) at Observable.push.../../node_modules/rxjs/_esm5/internal/Observable.js.Observable._trySubscribe (Observable.js:43) at Observable.push.../../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe (Observable.js:29) at MapOperator.push.../../node_modules/rxjs/_esm5/internal/operators/map.js.MapOperator.call (map.js:18) at Observable.push.../../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe (Observable.js:24) at CatchOperator.push.../../node_modules/rxjs/_esm5/internal/operators/catchError.js.CatchOperator.call (catchError.js:18)

TypeError: Cannot read property 'error' of undefined at directCallParentKarmaMethod (context.js:270) at ContextKarma.error (context.js:155) at handleGlobalErrors (adapter.js:176) at KarmaReporter.suiteDone (adapter.js:224) at dispatch (jasmine.js:4560) at ReportDispatcher.suiteDone (jasmine.js:4531) at nodeComplete (jasmine.js:1019) at onComplete (jasmine.js:5528) at ZoneDelegate.../../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:423) at Zone.../../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:195)

Expected function to throw an exception. at UserContext. (http://localhost:9876/src/app/shared/thumbnail/slideshow.directive.spec.ts?:227:6) at ZoneDelegate.../../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/C:/Users/uzivatel/Documents/nubium/ulozto-web/angular/node_modules/zone.js/dist/zone.js?:391:1) at ProxyZoneSpec.push.../../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (http://localhost:9876/C:/Users/uzivatel/Documents/nubium/ulozto-web/angular/node_modules/zone.js/dist/zone-testing.js?:289:1) at ZoneDelegate.../../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9876/C:/Users/uzivatel/Documents/nubium/ulozto-web/angular/node_modules/zone.js/dist/zone.js?:390:1)

Upvotes: 0

Views: 1292

Answers (1)

davidveen
davidveen

Reputation: 388

One possible solution is to redirect the error to the success path, then testing that value. This can be useful when the stream under test has logic connected to throwing different error types (though in such a case a test for instanceof Error would of course not be sufficient).

  it('should be of type error', () => {
    of(1)
      .pipe(
        map(() => {
          throw new Error('err');
        }),
        catchError(error => of(error)))
      .subscribe(result => {
        expect(result instanceof Error).toBe(true);
      });
  });

But a more common approach afaik is to test if the correct callback has been called, as mentioned by JB Nizet. E.g. your service has an handleError method that should be called when a stream errors.

Upvotes: 1

Related Questions