如何从 Jasmine 测试中执行 switchMap 和 tap rxJS 函数?
How to execute switchMap and tap rxJS functions from Jasmine test?
我有以下测试
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
let loader: HarnessLoader;
const items: MyItem[] = [
{
id: '66249535-31bf-41f3-8f55-8a14877c6d7e',
status: 'New'
}
];
const myServiceSpy = jasmine.createSpyObj<MyService>(
'MyService',
{
getItems: of(items),
changeItemStatus: of()
}
);
const alertsServiceSpy = jasmine.createSpyObj<AlertsService>('AlertsService', [
'error',
'warning',
'success',
'info'
]);
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MyComponent, TableComponent],
providers: [{ provide: MyService, useValue: myServiceSpy }, { provide: AlertsService, useValue: alertsServiceSpy}],
imports: [MatTableModule, SharedModule]
}).compileComponents();
}));
beforeEach(() => {
myServiceSpy.getItems.calls.reset();
fixture = TestBed.createComponent(MyComponent);
loader = TestbedHarnessEnvironment.loader(fixture);
component = fixture.componentInstance;
component.myId = 123;
fixture.detectChanges();
});
afterAll(() => {
myServiceSpy.getItems.and.returnValue(of(items));
});
it('should call to change the item status if the slider is toggled', async(() => {
component.onItemChange(items[0], { checked: true, event: { source: { checked: true }}});
component.onItemChange(items[0], { checked: false, event: { source: { checked: false }}});
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(myServiceSpy.changeItemStatus).toHaveBeenCalled();
});
}));
以及以下组件
ngOnInit() {
const updatedItems$ = this.itemChangeSubject.asObservable().pipe(
switchMap(itemChange => {
this.itemBeingChanged = itemChange.item;
this.toggleEvent = itemChange.event;
return this.myService.changeItemStatus(
this.myId,
itemChange.item.id
)
.pipe(catchError(() => {
this.clicked = false;
this.toggleEvent.source.checked = ! this.toggleEvent.source.checked;
return empty();
}));
}),
tap({
next: () => {
const itemStatus = this.itemBeingChanged.isItemChanged ? 'active' : new';
this.clicked = false;
this.alerts.success({
message: `Item is now ${itemStatus}.`
});
}
}),
switchMap(() => this.myService.getItems(
this.myId
)
.pipe(catchError(() => {
return empty();
}))
)
);
this.items$ = this.myService
.getItems(this.myId)
.pipe(catchError(() => {
return empty();
}))
.pipe(concat(updatedItems$))
}
onItemChange(item: MyItem, event) {
this.itemChangeSubject.next({ item, event });
}
我的测试按预期执行了 ngOnInit
中的第一个 switchMap
,但是既没有执行 tap 副作用,也没有执行以下 switchMap。我如何修改我的测试以便我可以测试整个链,而不仅仅是第一个 switchMap
?此外,是否有必要测试抽头或错误块?我最关心的是最终的 switchMap,因为它确实调用了服务。
我认为这是由于 changeItemStatus: of()
。
of
定义为 as follows:
return fromArray(args as T[]);
最终会达到 subscribeToArray
:
export const subscribeToArray = <T>(array: ArrayLike<T>) => (subscriber: Subscriber<T>) => {
for (let i = 0, len = array.length; i < len && !subscriber.closed; i++) {
subscriber.next(array[i]);
}
subscriber.complete();
};
所以,当使用 of()
时,上面的 array
将是空的,这意味着 Observable 不会发出任何东西。因此,将无法到达链中的下一个运算符。
如果将 of()
替换为 of(null)
,应该可以使用。
我有以下测试
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
let loader: HarnessLoader;
const items: MyItem[] = [
{
id: '66249535-31bf-41f3-8f55-8a14877c6d7e',
status: 'New'
}
];
const myServiceSpy = jasmine.createSpyObj<MyService>(
'MyService',
{
getItems: of(items),
changeItemStatus: of()
}
);
const alertsServiceSpy = jasmine.createSpyObj<AlertsService>('AlertsService', [
'error',
'warning',
'success',
'info'
]);
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MyComponent, TableComponent],
providers: [{ provide: MyService, useValue: myServiceSpy }, { provide: AlertsService, useValue: alertsServiceSpy}],
imports: [MatTableModule, SharedModule]
}).compileComponents();
}));
beforeEach(() => {
myServiceSpy.getItems.calls.reset();
fixture = TestBed.createComponent(MyComponent);
loader = TestbedHarnessEnvironment.loader(fixture);
component = fixture.componentInstance;
component.myId = 123;
fixture.detectChanges();
});
afterAll(() => {
myServiceSpy.getItems.and.returnValue(of(items));
});
it('should call to change the item status if the slider is toggled', async(() => {
component.onItemChange(items[0], { checked: true, event: { source: { checked: true }}});
component.onItemChange(items[0], { checked: false, event: { source: { checked: false }}});
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(myServiceSpy.changeItemStatus).toHaveBeenCalled();
});
}));
以及以下组件
ngOnInit() {
const updatedItems$ = this.itemChangeSubject.asObservable().pipe(
switchMap(itemChange => {
this.itemBeingChanged = itemChange.item;
this.toggleEvent = itemChange.event;
return this.myService.changeItemStatus(
this.myId,
itemChange.item.id
)
.pipe(catchError(() => {
this.clicked = false;
this.toggleEvent.source.checked = ! this.toggleEvent.source.checked;
return empty();
}));
}),
tap({
next: () => {
const itemStatus = this.itemBeingChanged.isItemChanged ? 'active' : new';
this.clicked = false;
this.alerts.success({
message: `Item is now ${itemStatus}.`
});
}
}),
switchMap(() => this.myService.getItems(
this.myId
)
.pipe(catchError(() => {
return empty();
}))
)
);
this.items$ = this.myService
.getItems(this.myId)
.pipe(catchError(() => {
return empty();
}))
.pipe(concat(updatedItems$))
}
onItemChange(item: MyItem, event) {
this.itemChangeSubject.next({ item, event });
}
我的测试按预期执行了 ngOnInit
中的第一个 switchMap
,但是既没有执行 tap 副作用,也没有执行以下 switchMap。我如何修改我的测试以便我可以测试整个链,而不仅仅是第一个 switchMap
?此外,是否有必要测试抽头或错误块?我最关心的是最终的 switchMap,因为它确实调用了服务。
我认为这是由于 changeItemStatus: of()
。
of
定义为 as follows:
return fromArray(args as T[]);
最终会达到 subscribeToArray
:
export const subscribeToArray = <T>(array: ArrayLike<T>) => (subscriber: Subscriber<T>) => {
for (let i = 0, len = array.length; i < len && !subscriber.closed; i++) {
subscriber.next(array[i]);
}
subscriber.complete();
};
所以,当使用 of()
时,上面的 array
将是空的,这意味着 Observable 不会发出任何东西。因此,将无法到达链中的下一个运算符。
如果将 of()
替换为 of(null)
,应该可以使用。