12

如何对动作是否已发送进行单元测试?

例如,在 LogoutService 中,我有这个简单的方法:

  logout(username: string) {
    store.dispatch([new ResetStateAction(), new LogoutAction(username)]);
  }

我需要编写一个单元测试来验证这两个操作是否已分派:

  it('should dispatch ResetState and Logout actions', function () {
    logoutService.logout();

    // how to check the dispactched actions and their parameters?
    // expect(...)
  });

如何检查已调度的操作?

4

3 回答 3

15

NGXS 可管道操作符

NGXS 中的操作由 Observables 处理。NGXS 为您提供 Pipeable Operators,您可以在测试中使用ofActionDispatched. 这是我从NGXS 文档中获取的列表:

  • ofAction当以下任何生命周期事件发生时触发
  • ofActionDispatched派发动作时触发
  • ofActionSuccessful当动作成功完成时触发
  • ofActionCanceled当动作被取消时触发
  • ofActionErrored当操作导致抛出错误时触发
  • ofActionCompleted当动作完成时触发,无论它是否成功(返回完成摘要)

回答

1.创建变量actions$

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

  // ...
});

actions$2.用 observable初始化变量

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

3.1 测试是否调用了 1 个动作:

使用 operator 从流中过滤您的操作ofActionsDispatched()

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

  service.logout();
});

3.2 测试是否调用了多个动作:

使用 RXJS zip 运算符将两个 observable 与ofActionsDispatched()函数组合(zip:在所有observable 发出之后,将值作为数组发出)。

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

  service.logout();
});

done在调用规范之前,规范不会完成。如果done不调用,将抛出超时异常。

来自Jasmine 文档

于 2018-08-02T08:50:14.237 回答
1

使用茉莉花间谍

我相信在单元测试中,所有相关依赖项的实际实现都应该被模拟,因此我们不应该在这里包含任何实际的存储。在这里,我们为 Store 提供了一个 jasmine spy,并且只是检查是否使用正确的参数调度了某些操作。这也可以用于提供存根数据。

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();
});
于 2020-11-09T05:58:01.547 回答
0

我尝试了这种方法来测试是否调用了这两个操作:

3. 测试是否正在调用动作

// ...
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)
    );
}));
// ...
于 2019-02-22T17:11:36.373 回答