如何从 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),应该可以使用。