NGXS: How to test if an action was dispatched?

前端 未结 3 2187
遇见更好的自我
遇见更好的自我 2021-02-14 06:57

How to unit test whether an action was dispatched?

For example, in a LogoutService, I have this simple method:

  logout(username: string) {
    store.dis         


        
相关标签:
3条回答
  • 2021-02-14 07:13

    NGXS Pipeable Operators

    Actions in NGXS are handled with Observables. NGXS provides you Pipeable Operators, for your test you could use the ofActionDispatched. Here is the list I have taken from the NGXS documentation:

    • ofAction triggers when any of the below lifecycle events happen
    • ofActionDispatched triggers when an action has been dispatched
    • ofActionSuccessful triggers when an action has been completed successfully
    • ofActionCanceled triggers when an action has been canceled
    • ofActionErrored triggers when an action has caused an error to be thrown
    • ofActionCompleted triggers when an action has been completed whether it was successful or not (returns completion summary)

    Answer

    1. Create variable actions$

    describe('control-center.state', () => {
      let actions$: Observable<any>;
    
      // ...
    });
    

    2. Initialize variable actions$ with observable

    beforeEach(() => {
      TestBed.configureTestingModule({
        imports: [
          NgxsModule.forRoot([AppState]),
          NgxsModule.forFeature([ControlCenterState])
        ]
      });
      store = TestBed.get(Store);
      actions$ = TestBed.get(Actions);
    })
    

    3.1 Test if 1 action has been called:

    Filter your actions from the stream with the operator ofActionsDispatched().

    it('should dispatch LogoutAction', (done) => {
      actions$.pipe(ofActionDispatched(LogoutAction)).subscribe((_) => {
        done();
      });
    
      service.logout();
    });
    

    3.2 Test if multiple actions have been called:

    Use the RXJS zip operator to combine the two observables with the ofActionsDispatched() function (zip: after all observables emit, emit values as an array).

    it('should dispatch ResetStateAction and LogoutAction', (done) => {
      zip(
        actions$.pipe(ofActionDispatched(ResetStateAction)),
        actions$.pipe(ofActionDispatched(LogoutAction))
      ).subscribe((_) => {
        done();
      });
    
      service.logout();
    });
    

    The spec will not complete until its done is called. If done is not called a timeout exception will be thrown.

    From the Jasmine documentation.

    0 讨论(0)
  • 2021-02-14 07:17

    Using Jasmine Spies

    I believe that in unit testing the actual implementation of all the related dependencies should be mocked and hence we should not be including any actual stores in here. Here we are providing a jasmine spy for Store and just checking whether certain actions are dispatched with correct parameters. This could also be used to provide stub data too.

    describe('LogoutService', () => {
      let storeSpy: jasmine.SpyObj<Store>;
    
      beforeEach(() => {
        storeSpy = jasmine.createSpyObj(['dispatch']);
    
        TestBed.configureTestingModule({
          providers: [LogoutService, { provide: Store, useValue: storeSpy }]
        });
      })
    
      it('should dispatch Logout and Reset actions', () => {
        storeSpy.dispatch.withArgs([
          jasmine.any(ResetStateAction), 
          jasmine.any(LogoutAction)])
         .and
         .callFake(([resetAction, logoutAction]) => {
           expect(resetAction.payload).toEqual({...something});
           expect(logoutAction.payload).toEqual({...somethingElse});
        });
    
        TestBed.inject(LogoutService).logout();
    });
    
    0 讨论(0)
  • 2021-02-14 07:19

    I tried this approach to test if both actions were called:

    3. Test if actions are being called

    // ...
    it('should call actions ResetStateAction and LogoutAction', async( () => {
      let actionDispatched = false;
      zip(
        actions$.pipe(ofActionDispatched(ResetStateAction)),
        actions$.pipe(ofActionDispatched(LogoutAction))
      )
      .subscribe( () => actionDispatched = true );
    
      store.dispatch([new ResetStateAction(), new LogoutAction()])
        .subscribe(
          () => expect(actionDispatched).toBe(true)
        );
    }));
    // ...
    
    0 讨论(0)
提交回复
热议问题