admin管理员组

文章数量:1123712

I defined a number of effects, whose first one has dispatch: false:

    readonly abort$ = createEffect(() => {
        return this.actions.pipe(
            ofType(myActions.abort),
            tap( (action) => {
                console.log('request abort for', action.reason.toString());
                ...
            }),
            catchError(MyEffects.catchServiceError)
        )
    }, { dispatch: false } );

I defined unit tests like following:

describe('MyBankEffects', () => {
    let effects: MyBankEffects;
    let action$: Observable<Action> = new Observable<Action>();
    beforeEach( () => {
        TestBed.configureTestingModule({
            imports: [
                EffectsModule.forFeature(MyBankEffects)
            ],
            providers: [
                provideMockActions( () => action$),
            ]
        });
        effects = TestBed.inject(MyBankEffects);
    });

    it('should navigate to abort', (done) => {
        action$ = of(myActions.abort({ reason: AbortReason.TIMEOUT }));
        effects.abort$.pipe(take(1)).subscribe({
            next: () => {
                expect(...);
            },
            error: e => done.fail(e),
            complete: () => {
                expect(...);
                done();
            }
        });
    });

In the next executed test, whichever it is, the first effect is executed again (without any mocks defined in its specific test), before running the test itself. It looks like the action dispatched in the test above, remains "in queue", and is consumed by the following test execution.

Anyone can explain what happens, and how can I purge the action at the end of the single test?

I defined a number of effects, whose first one has dispatch: false:

    readonly abort$ = createEffect(() => {
        return this.actions.pipe(
            ofType(myActions.abort),
            tap( (action) => {
                console.log('request abort for', action.reason.toString());
                ...
            }),
            catchError(MyEffects.catchServiceError)
        )
    }, { dispatch: false } );

I defined unit tests like following:

describe('MyBankEffects', () => {
    let effects: MyBankEffects;
    let action$: Observable<Action> = new Observable<Action>();
    beforeEach( () => {
        TestBed.configureTestingModule({
            imports: [
                EffectsModule.forFeature(MyBankEffects)
            ],
            providers: [
                provideMockActions( () => action$),
            ]
        });
        effects = TestBed.inject(MyBankEffects);
    });

    it('should navigate to abort', (done) => {
        action$ = of(myActions.abort({ reason: AbortReason.TIMEOUT }));
        effects.abort$.pipe(take(1)).subscribe({
            next: () => {
                expect(...);
            },
            error: e => done.fail(e),
            complete: () => {
                expect(...);
                done();
            }
        });
    });

In the next executed test, whichever it is, the first effect is executed again (without any mocks defined in its specific test), before running the test itself. It looks like the action dispatched in the test above, remains "in queue", and is consumed by the following test execution.

Anyone can explain what happens, and how can I purge the action at the end of the single test?

Share Improve this question asked yesterday HilbertHilbert 335 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

This happens because the action observable(action$) remains active across the test cases, and the dispatched action (myActions.abort) is not being completely cleaned up after the test.

You can ensure that actions are properly consumed and the action$ observable is reset for each test by:

  1. Using new instance of action$ for each test.

  2. Completing the observable after the test.

  3. Cleaning up subscriptions or ensuring that the effect itself unsubscribes.

     import { TestBed } from '@angular/core/testing';
     import { provideMockActions } from '@ngrx/effects/testing';
     import { Observable, of, Subject } from 'rxjs';
     import { take } from 'rxjs/operators';
     import { MyBankEffects } from './my-bank.effects';
     import * as myActions from './my-bank.actions';
     import { AbortReason } from './abort-reason.enum';
    
     describe('MyBankEffects', () => {
         let effects: MyBankEffects;
         let action$: Subject<Action>;
    
         beforeEach(() => {
             action$ = new Subject<Action>();
             TestBed.configureTestingModule({
                 imports: [
                     EffectsModule.forFeature([MyBankEffects])
                 ],
                 providers: [
                     provideMockActions(() => action$),
                 ],
             });
             effects = TestBed.inject(MyBankEffects);
         });
    
         afterEach(() => {
             // Complete the observable after each test to avoid cross-contamination.
             action$.complete();
         });
    
         it('should navigate to abort', (done) => {
             const reason = AbortReason.TIMEOUT;
             action$.next(myActions.abort({ reason }));
    
             effects.abort$.pipe(take(1)).subscribe({
                 next: () => {
                     expect(true).toBe(true); // Replace with your actual assertion.
                 },
                 error: (e) => done.fail(e),
                 complete: () => {
                     expect(true).toBe(true); // Replace with your actual assertion.
                     done();
                 },
             });
         });
     });
    

Key Changes:

  1. Subject for action$: This allows to emit actions(action$.next(...)) and complete the stream after each test(action$.complete()).

  2. Reset between Tests: afterEach ensures the Subject is completed, preventing leftover actions from being processed by subsequent tests.

  3. Avoid stale Observables: take(1) operator ensures that the effect only proccesses one action per test case.

本文标签: Angular unit test of ngrxeffectsactions not purgedStack Overflow