Angular NGXS:如何测试是否已调度操作?
如何单元测试是否已调度操作 例如,在LogoutService中,我有一个简单的方法:Angular NGXS:如何测试是否已调度操作?,angular,unit-testing,ngxs,Angular,Unit Testing,Ngxs,如何单元测试是否已调度操作 例如,在LogoutService中,我有一个简单的方法: logout(username: string) { store.dispatch([new ResetStateAction(), new LogoutAction(username)]); } 我需要编写一个单元测试来验证两个操作是否已调度: it('should dispatch ResetState and Logout actions', function () { l
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(...)
});
如何检查已调度的操作?NGXS可管道运算符
NGXS中的操作是使用可观察对象处理的。NGXS为您提供了可管道化操作符,对于您的测试,您可以使用ActionDispatched的。以下是我从以下列表中选择的:
ofAction
在以下任何生命周期事件发生时触发
- 动作已调度时触发动作调度的
ofActionDispatched
- 操作完成后,将触发操作成功的
OFACTIONSUCCESS
成功地
- 取消操作后,将触发ActionCancelled的
OFActionCancelled
- 当某个操作导致错误被删除时,将触发ActionErrored的
扔
- 操作完成时触发操作完成
是否成功(返回完成摘要)
答复
1。创建变量操作$
describe('control-center.state', () => {
let actions$: Observable<any>;
// ...
});
3.1如果调用了一个操作,则测试:
使用ActionsDispatched()的运算符从流中筛选您的操作
3.2测试是否调用了多个操作:
使用RXJS zip运算符将两个观察值与ActionsDispatched()的函数组合起来(zip:afterall观察值发射,将值作为数组发射)
在调用其done
之前,规范不会完成。如果未调用done
,将引发超时异常
在.中,我尝试了这种方法来测试是否调用了这两个操作:
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)
);
}));
// ...
使用茉莉花间谍
我相信在单元测试中,所有相关依赖项的实际实现都应该被模拟,因此我们不应该在这里包含任何实际的存储。
在这里,我们为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();
});
description('LogoutService',()=>{
让商店间谍:jasmine.SpyObj;
在每个之前(()=>{
storeSpy=jasmine.createSpyObj(['dispatch']);
TestBed.configureTestingModule({
提供者:[LogoutService,{Provider:Store,useValue:storeSpy}]
});
})
它('应发送注销和重置操作',()=>{
storeSpy.dispatch.withArgs([
jasmine.任何(行动),
茉莉花.任何(LogoutAction)])
.及
.callFake(([resetAction,logoutAction])=>{
expect(resetAction.payload).toEqual({…something});
expect(logoutAction.payload).toEqual({…somethingElse});
});
TestBed.inject(LogoutService.logout();
});
创建存储区的模拟,并检查是否使用相应的NG参数调用了dispatched一次。我还没有在使用NGXS进行测试时尝试过它,但是您可以订阅以在这些操作被调度时收到通知吗?我已经完成了@GarthMason提到的操作,并且它可以工作。谢谢!我已经测试了此实现,并且有一个sOfcationDispatched筛选器中出现小错误,应按如下方式调用:OfcationDispatched(ResetStateAction,LogoutAction)。请注意,不应将操作包装在列表中。此外,不应需要“完成”函数。如果像上面那样实现,则测试实际上会错误通过。将测试包装在异步中会按预期工作。@ActionDispatched的salomonvh确实获取了一个列表,并对其进行了更改。关于下一条语句,您需要“完成”函数,否则它会被删除will Falsy pass:如果通过了done函数,测试预期会调用它,如果在x秒内没有调用它,测试将失败。我不理解测试的重要性。您可以调度2个操作,然后测试它们是否被调度。@bndamm我测试中的调度调用只是一个示例。在实际场景中,您可能会测试一个方法或第三个不同的操作在执行过程中是否有效地调用了感兴趣的操作。@ClaudioSuardi您所说的ofActionDispatched“似乎等于ResetStateAction或LogoutAction”。这是不正确的,文档:这将只抓取刚刚分派的操作
,它只过滤掉您传递的操作。@Brampage我明白您的意思。我更改了答案以避免遗漏错误信息。
it('should dispatch ResetStateAction and LogoutAction', (done) => {
zip(
actions$.pipe(ofActionDispatched(ResetStateAction)),
actions$.pipe(ofActionDispatched(LogoutAction))
).subscribe((_) => {
done();
});
service.logout();
});
// ...
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)
);
}));
// ...
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();
});