javascript - What is the correct way of testing chained catchError functions - Stack Overflow

I'm trying to write a jasmine test for an @Effect that has chained rxjs catchError operators, but

I'm trying to write a jasmine test for an @Effect that has chained rxjs catchError operators, but am struggling to test any observables beyond the first catchError.

here's the effect:

@Effect() submitEndsheets$ = this.actions$.pipe(
    ofType<SubmitEndSheets>(SpreadActionTypes.SUBMIT_ENDSHEETS),
    withLatestFrom(this.store.pipe(select(fromAppStore.fromOrder.getDocumentId))),
    concatMap(([action, documentId]) =>
        this.spreadService.submitEndSheets(documentId).pipe(
            map((response: ActionProcessorDto) => new SubmitEndSheetsSuccess(response.data)),
            catchError((error) => of(undo(action))),
            catchError((error) => of(new MessageModal({
                    message: error.message,
                    title: 'Submission Error!'
                })
            ))
        )
    )
);

and the corresponding test:

it('handles errors by sending an undo action', () => {
        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);
    });

for reference, here are the createServiceStub that mocks the service and createStoreState that, you guessed it, creates a mock store.

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;
}

here's the test output:

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 pare node_modules/jasmine-marbles/bundles/jasmine-marbles.umd.js:389:1)
          at UserContext.<anonymous> 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)

Thanks in advance for any help!

Update: catchError can send an array of actions back out of the effect like so:

@Effect() submitEndsheets$ = this.actions$.pipe(
    ofType<SubmitEndSheets>(SpreadActionTypes.SUBMIT_ENDSHEETS),
    withLatestFrom(this.store.pipe(select(fromAppStore.fromOrder.getDocumentId))),
    concatMap(([action, documentId]) =>
        this.spreadService.submitEndSheets(documentId).pipe(
            map((response: ActionProcessorDto) => new SubmitEndSheetsSuccess(response.data)),
            catchError(error => [
                new PopSingleToast({
                    severity: ToastSeverity.error,
                    summary: 'Failure',
                    detail: `Some error occurred: \n Error: ${error}`
                }),
                undo(action)
            ])
        )
    )
);

The corresponding test looks like so:

it('handles errors by sending an undo action', () => {
        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);
    });

Thanks for to all for the help!

I'm trying to write a jasmine test for an @Effect that has chained rxjs catchError operators, but am struggling to test any observables beyond the first catchError.

here's the effect:

@Effect() submitEndsheets$ = this.actions$.pipe(
    ofType<SubmitEndSheets>(SpreadActionTypes.SUBMIT_ENDSHEETS),
    withLatestFrom(this.store.pipe(select(fromAppStore.fromOrder.getDocumentId))),
    concatMap(([action, documentId]) =>
        this.spreadService.submitEndSheets(documentId).pipe(
            map((response: ActionProcessorDto) => new SubmitEndSheetsSuccess(response.data)),
            catchError((error) => of(undo(action))),
            catchError((error) => of(new MessageModal({
                    message: error.message,
                    title: 'Submission Error!'
                })
            ))
        )
    )
);

and the corresponding test:

it('handles errors by sending an undo action', () => {
        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);
    });

for reference, here are the createServiceStub that mocks the service and createStoreState that, you guessed it, creates a mock store.

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;
}

here's the test output:

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 pare node_modules/jasmine-marbles/bundles/jasmine-marbles.umd.js:389:1)
          at UserContext.<anonymous> 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)

Thanks in advance for any help!

Update: catchError can send an array of actions back out of the effect like so:

@Effect() submitEndsheets$ = this.actions$.pipe(
    ofType<SubmitEndSheets>(SpreadActionTypes.SUBMIT_ENDSHEETS),
    withLatestFrom(this.store.pipe(select(fromAppStore.fromOrder.getDocumentId))),
    concatMap(([action, documentId]) =>
        this.spreadService.submitEndSheets(documentId).pipe(
            map((response: ActionProcessorDto) => new SubmitEndSheetsSuccess(response.data)),
            catchError(error => [
                new PopSingleToast({
                    severity: ToastSeverity.error,
                    summary: 'Failure',
                    detail: `Some error occurred: \n Error: ${error}`
                }),
                undo(action)
            ])
        )
    )
);

The corresponding test looks like so:

it('handles errors by sending an undo action', () => {
        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);
    });

Thanks for to all for the help!

Share edited Jan 22, 2019 at 15:15 mpaarating asked Jan 19, 2019 at 14:54 mpaaratingmpaarating 532 silver badges8 bronze badges 2
  • Placing catchError two times in a row doesn't make sense, only* the first handler will be used. Are you trying to emit/perform 2 actions in row if an error occurs? – Oles Savluk Commented Jan 19, 2019 at 21:23
  • @OlesSavluk you are 100% correct. For some reason, I was forgetting to throw the error to fire the second catchError. I updated my post with the solution that worked for my use case. – mpaarating Commented Jan 20, 2019 at 4:40
Add a ment  | 

1 Answer 1

Reset to default 6

Having two catchErrors in a row like this means the second one will never trigger because the first one will eat the error.

You would need to rethrow the error in the first catchError to get inside the second one:

catchError(error => throw new Error()),
catchError(error => console.log('now I trigger'))

So I am afraid your questions doesn't make really sense.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744914315a4600740.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信