NgRx 测试 - 检查调度动作的顺序

NgRx Testing - Check order of dispatched actions

我在 运行 单元测试时模拟我的 NgRx 存储并在 store.dispatch() 函数上创建 Jasmine 间谍。
有了这个,我当然可以验证:
一个。通过 store.dispatch() 和
发送了一个特定的动作 b.模拟的 store.dispatch() 函数被调用的次数。

这在以下代码段中可见:

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

不过,我想知道,有没有办法验证动作的调度顺序?

以上只是一个简单的单元测试示例,但如果运行稍后进行集成测试,我想有一种方法来验证不同动作的调度顺序会很有用。

我已经尝试直接访问调度模拟中的 'calls' 列表(就像在 Jest 中可以完成的那样),但我猜 Jasmine 不支持这个(调用时没有得到任何智能提示) .something on storeMock.dispatch on the next line)

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

我无法在主要的 NgRx 文档、此处的 Whosebug 或通过任何其他方式找到与此相关的任何内容。

我们可以用 Jasmine 验证 NgRx 中调度调用的顺序吗?

如有任何想法,我们将不胜感激。

尝试使用 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()]); 
      })
 );

使用 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()
        );
      })
 );

我会在@AliF50 的回答中添加建议如何检查难以重新创建对象且您只想检查类型的复杂操作:

const spyDispatch = spyOn(store, 'dispatch');

想法是使用 jasmine.objectContaining

const complexActionType = ComplexAction.type;
expect(spyDispatch).toHaveBeenCalledWith(jasmine.objectContaining({ type: complexActionType }))