Search code examples
angularunit-testingmockingjasminengrx

NgRx Testing - Check order of dispatched actions


I am mocking my NgRx store when running unit tests and creating a Jasmine spy on the store.dispatch() function.
With this, I am of course able to verify that:
a. an specific action was dispatched via store.dispatch() and
b. the number of times the mocked store.dispatch() function was called.

This is seen in the following snippet:

let storeMock;
beforeEach(() => {
  storeMock = {
    dispatch: jasmine.createSpy('dispatch')
  };

  TestBed.configureTestingModule({
    imports: [],
    declarations: [HeaderComponent],
    providers: [
      { provide: Store, useValue: storeMock }
    ]
  });
  TestBed.compileComponents();
});

it(`should dispatch "Loading" and "StoreRecipes" actions when button is clicked`,
      async(() => {
        const fixture = TestBed.createComponent(HeaderComponent);
        fixture.detectChanges();
        const debugElement = queryToGetButton();
        debugElement.nativeElement.click();

        // this works as expected, tests are passing
        expect(storeMock.dispatch).toHaveBeenCalledTimes(2);
        expect(storeMock.dispatch).toHaveBeenCalledWith(
          new RecipeActions.Loading()
        );
        expect(storeMock.dispatch).toHaveBeenCalledWith(
          new RecipeActions.FetchRecipes()
        );
      })
 );

I am wondering, however, is there is a way to verify the order in which the actions are dispatched?

The above is just a simple unit test example, but if running integration testing later, I imagine it would be useful to have a way to verify the order in which different actions were dispatched.

I have tried accessing the 'calls' list on the dispatch mock directly (like can be done in Jest), but am guessing this isn't supported in Jasmine (wasn't getting any intellisense suggestion when calling .something on storeMock.dispatch on the next line)

expect(storeMock.dispatch.calls).toEqual([
  [new RecipeActions.Loading()], // First call
  [new RecipeActions.FetchRecipes()]  // Second call
]);

I haven't been able to find anything related to this on the main NgRx documentation, here on StackOverflow or through any other means.

Can we verify the order of dispatch calls in NgRx with Jasmine?

Any ideas would be much appreciated.


Solution

  • Try using calls.all(). https://daveceddia.com/jasmine-2-spy-cheat-sheet/

    it(`should dispatch "Loading" and "StoreRecipes" actions when button is clicked`,
          async(() => {
            const fixture = TestBed.createComponent(HeaderComponent);
            fixture.detectChanges();
            const debugElement = queryToGetButton();
            debugElement.nativeElement.click();
    
            // this works as expected, tests are passing
            expect(storeMock.dispatch).toHaveBeenCalledTimes(2);
            expect(storeMock.dispatch).toHaveBeenCalledWith(
              new RecipeActions.Loading()
            );
            expect(storeMock.dispatch).toHaveBeenCalledWith(
              new RecipeActions.FetchRecipes()
            );
            // new code
            const allCalls = storeMock.dispatch.calls.all();
            console.log(allCalls);
            const firstCall = allCalls[0].args;
            const secondCall = allCalls[1].args;
            expect(firstCall).toEqual([new RecipeActions.Loading()]);
            expect(secondCall).toEqual([new RecipeActions.FetchRecipes()]); 
          })
     );
    

    Alternative way using callFake:

    it(`should dispatch "Loading" and "StoreRecipes" actions when button is clicked`,
          async(() => {
            const fixture = TestBed.createComponent(HeaderComponent);
            fixture.detectChanges();
            const debugElement = queryToGetButton();
            // new code
            let dispatchCount = 0;
            storeMock.dispatch.and.callFake(action => {
              dispatchCount++;
              if (dispatchCount === 1) {
                console.log('first dispatch', action);
                expect(action).toEqual(new RecipeActions.Loading());
              } else {
                console.log('2nd dispatch', action);
                expect(action).toEqual(new RecipeActions.FetchRecipes());
              }
            });
            debugElement.nativeElement.click();
    
            // this works as expected, tests are passing
            expect(storeMock.dispatch).toHaveBeenCalledTimes(2);
            expect(storeMock.dispatch).toHaveBeenCalledWith(
              new RecipeActions.Loading()
            );
            expect(storeMock.dispatch).toHaveBeenCalledWith(
              new RecipeActions.FetchRecipes()
            );
          })
     );