【问题标题】:How to execute switchMap and tap rxJS functions from Jasmine test?如何从 Jasmine 测试中执行 switchMap 和点击 rxJS 函数?
【发布时间】:2020-10-29 03:35:16
【问题描述】:

我有以下测试

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,但没有执行点击副作用或以下switchMap。如何修改我的测试以便我可以测试整个链,而不仅仅是第一个switchMap?此外,是否有必要测试抽头或错误块?我最关心的是最终的 switchMap,因为它确实调用了服务。

【问题讨论】:

    标签: angular rxjs jasmine switchmap


    【解决方案1】:

    我想是因为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),它应该可以工作。

    【讨论】:

    • 我用of([]) 代替了它,因为这通常会返回一个数组并且它有效!也谢谢你的解释,现在说得通了。
    猜你喜欢
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多