title: What is the correct way of testing chained catchError functions
body: <p>I'm trying to write a jasmine test for an @Effect that has chained rxjs <code>catchError</code> operators, but am struggling to test any observables beyond the first <code>catchError</code>.</p>
  
  <p>here's the effect:</p>
  
  <pre><code>@Effect() submitEndsheets$ = this.actions$.pipe(
      ofType&lt;SubmitEndSheets&gt;(SpreadActionTypes.SUBMIT_ENDSHEETS),
      withLatestFrom(this.store.pipe(select(fromAppStore.fromOrder.getDocumentId))),
      concatMap(([action, documentId]) =&gt;
          this.spreadService.submitEndSheets(documentId).pipe(
              map((response: ActionProcessorDto) =&gt; new SubmitEndSheetsSuccess(response.data)),
              catchError((error) =&gt; of(undo(action))),
              catchError((error) =&gt; of(new MessageModal({
                      message: error.message,
                      title: 'Submission Error!'
                  })
              ))
          )
      )
  );
  </code></pre>
  
  <p>and the corresponding test:</p>
  
  <pre><code>it('handles errors by sending an undo action', () =&gt; {
          const action = {
              type: SpreadActionTypes.SUBMIT_ENDSHEETS,
          };
          const source = cold('a', { a: action });
          const error = new Error('Error occurred!');
          const service = createServiceStub(error);
          const store = createStoreState();
          const effects = new Effects(service, new Actions(source), store);
  
          const expected = cold('ab', {
             a: undo(action),
              b: new MessageModal({
                  message: 'Sorry, something went wrong with your request. Please try again or contact support.',
                  title: 'Update Error!'
              }),
          });
          expect(effects.submitEndsheets$).toBeObservable(expected);
      });
  </code></pre>
  
  <p>for reference, here are the <code>createServiceStub</code> that mocks the service and <code>createStoreState</code> that, you guessed it, creates a mock store.</p>
  
  <pre><code>function createServiceStub(response: any) {
      const service = jasmine.createSpyObj('spreadService', [
          'load',
          'update',
          'updateSpreadPosition',
          'submitEndSheets'
      ]);
  
      const isError = response instanceof Error;
      const serviceResponse = isError ? throwError(response) : of(response);
  
      service.load.and.returnValue(serviceResponse);
      service.update.and.returnValue(serviceResponse);
      service.updateSpreadPosition.and.returnValue(serviceResponse);
      service.submitEndSheets.and.returnValue(serviceResponse);
  
      return service;
  }
  
  function createStoreState() {
      const store = jasmine.createSpyObj('store', ['pipe']);
      store.pipe.and.returnValue(of({ documentId: 123 }));
  
      return store;
  }
  </code></pre>
  
  <p>here's the test output:</p>
  
  <pre><code>FAILED TESTS:
        ✖ handles errors by sending an undo action
          HeadlessChrome 0.0.0 (Mac OS X 10.14.2)
        Expected $.length = 1 to equal 2.
        Expected $[1] = undefined to equal Object({ frame: 10, notification: Notification({ kind: 'N', value: MessageModal({ payload: Object({ message: 'Sorry, something went wrong with your request. Please try again or contact support.', title: 'Update Error!' }), type: 'MESSAGE_MODAL' }), error: undefined, hasValue: true }) }).
            at compare node_modules/jasmine-marbles/bundles/jasmine-marbles.umd.js:389:1)
            at UserContext.&lt;anonymous&gt; src/app/book/store/spread/spread.effects.spec.ts:197:46)
            at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke node_modules/zone.js/dist/zone.js:388:1)
  </code></pre>
  
  <p>Thanks in advance for any help!</p>
  
  <p><strong>Update:</strong>
  <code>catchError</code> can send an array of actions back out of the effect like so:</p>
  
  <pre><code>@Effect() submitEndsheets$ = this.actions$.pipe(
      ofType&lt;SubmitEndSheets&gt;(SpreadActionTypes.SUBMIT_ENDSHEETS),
      withLatestFrom(this.store.pipe(select(fromAppStore.fromOrder.getDocumentId))),
      concatMap(([action, documentId]) =&gt;
          this.spreadService.submitEndSheets(documentId).pipe(
              map((response: ActionProcessorDto) =&gt; new SubmitEndSheetsSuccess(response.data)),
              catchError(error =&gt; [
                  new PopSingleToast({
                      severity: ToastSeverity.error,
                      summary: 'Failure',
                      detail: `Some error occurred: \n Error: ${error}`
                  }),
                  undo(action)
              ])
          )
      )
  );
  </code></pre>
  
  <p>The corresponding test looks like so:</p>
  
  <pre><code>it('handles errors by sending an undo action', () =&gt; {
          const action = {
              type: SpreadActionTypes.SUBMIT_ENDSHEETS
          };
          const source = cold('a', { a: action });
          const error = new Error('Error occurred!');
          const service = createServiceStub(error);
          const store = createStoreState();
          const effects = new Effects(service, new Actions(source), store);
  
          const expectedAction = new PopSingleToast({
              severity: ToastSeverity.error,
              summary: 'Failure',
              detail: `Some error occurred: \n Error: ${error}`
          });
  
          const expected = cold('(ab)', {
              a: expectedAction,
              b: undo(action)
          });
  
          expect(effects.submitEndsheets$).toBeObservable(expected);
      });
  </code></pre>
  
  <p>Thanks for to all for the help!</p>