4

I am using EMPTY from rxjs in order to handle the catchError, What is the correct value for expected in order to pass the fail scenario.

import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { EMPTY } from 'rxjs';
import { map, mergeMap, catchError } from 'rxjs/operators';
import { MoviesService } from './movies.service';

@Injectable()
export class MovieEffects {

  loadMovies$ = createEffect(() => this.actions$.pipe(
    ofType('[Movies Page] Load Movies'),
    mergeMap(() => this.moviesService.getAll()
      .pipe(
        map(movies => ({ type: '[Movies API] Movies Loaded Success', payload: movies })),
        catchError(() => EMPTY)
      ))
    )
  );

  constructor(
    private actions$: Actions,
    private moviesService: MoviesService
  ) {}
}


// unit test

it('should return a empty observable', () => {
   this.moviesServiceSpy.and.return(throwError('Error in service'));

   action$ = hot('a', a: { loadMovies() });

   const expected = cold('|');

   expect(loadMovies$).toBeObservable(expected);

})
Tabares
  • 4,083
  • 5
  • 40
  • 47

1 Answers1

3

I just ran into this myself and stumbled upon your question. I think I have an answer: Since EMPTY immediately completes I took it to mean that 0 time passes. (EDIT: This was wrong!)

EMPTY or empty() by itself will match cold('|') or hot('|'). Read below for why it matches cold('') or hot('') in effects. (See the Examples section of this RxJS documentation, which shows this as well.)

Confirmed by a similar answer on another question, and now quoting the reason:

The pipe character in cold('|') represents the completion of the observable stream. However, your effect will not complete. The empty() observable does complete, but returning empty() from switchMap just sees that observable merged into the effect observable's stream - it does not complete the effect observable.

So while EMPTY (or empty()) is documented to complete immediately, the end-result of its use in the effect indicates that the effect never completes.

Plugging this answer into your example:

it('should return a empty observable', () => {
   this.moviesServiceSpy.and.return(throwError('Error in service'));

   action$ = hot('a', a: { loadMovies() });

   const expected = cold('');

   expect(loadMovies$).toBeObservable(expected);

})

And just for fun, the following test passes too:

it('should match EMPTY with a single tick pipe marble', () => {
   expect(EMPTY).toBeObservable(cold('|'));
   expect(EMPTY).toBeObservable(hot('|'));

   // empty() is deprecated, but these assertions still pass.
   expect(empty()).toBeObservable(cold('|'));
   expect(empty()).toBeObservable(hot('|'));
});
Doctor Blue
  • 3,769
  • 6
  • 40
  • 63