【问题标题】:How to test the side effects of a subscription in an Angular component如何在 Angular 组件中测试订阅的副作用
【发布时间】:2020-02-28 13:05:22
【问题描述】:

我有一个 Angular 组件,MyComponent,它调用一个返回 Observable 的方法并订阅它,类似于

export class MyComponent {
  someProperty;
  constructor(service: MyService) {}
  someButtonClicked() {
    this.service.doStuffAsync().subscribe(
      resp => this.someProperty = resp;
    );
  }
}

@Injectable()
export MyService {
  doStuffAsync() {
    // returns an Observable which notifies asychronously, e.g. like HttoClient.get(url)
  }
}

我想测试 someButtonClicked() 方法,因此我创建了一个 MyServiceMock 类,我将其注入到测试中

export class MyServiceMock {
  doStuffAsync() {
    return of({// some object}).pipe(observeOn(asyncScheduler));
  }
}

无论出于何种原因,我希望 MyServiceMockdoStuffAsync() 是异步的,因此使用了 observeOn(asyncScheduler)

此时虽然我不知道如何测试someButtonClicked()。我尝试了不同的策略,例如以下

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      providers: [
        { provide: MyService, useClass: MyServiceMock },
      ]
    }).compileComponents();
  }));

  let fixture: ComponentFixture<MyComponent>;
  let component: MyComponent;

  beforeEach(() => {
    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });
  it('test someButtonClicked', async(() => {
    component.someButtonClicked();
    fixture.whenStable().then(() => {
      expect(component.someProperty).toBeDefined();
    });
  }));

但由于 MyServiceMockdoStuffAsync() 异步,此操作失败。

所以我的问题是,哪个是测试订阅异步 Observable 的方法引起的副作用(即 someProperty 设置正确)的最佳策略。

【问题讨论】:

    标签: angular rxjs angular-test angular-observable


    【解决方案1】:
     // All code should be synchronous within this it block
        it('test someButtonClicked', async(() => {
          // sync - button clicks are synchronous
          component.someButtonClicked();
    
          // but after btn clicks, we need to let some async code to finish
          // like rxjs subscriptions etc.
          await fixture.whenStable();
          // and then continue
    
          expect(component.someProperty).toBeDefined();
        }));
    

    不要使用fixture.whenStable().then()

    【讨论】:

      【解决方案2】:

      我使用了一个名为 emit 的辅助函数

      export const emitted = obs$ => new Promise(function(resolve, reject) {
        const emitted$ = new Subject();
        obs$.pipe(takeUntil(emitted$)).subscribe(
          _ => {
            emitted$.next();
            emitted$.complete();
            resolve(true);
          },
          _ => {
            emitted$.next();
            emitted$.complete();
            reject('An error occurred');
          }
        )
      });
      

      如果您的模拟服务与组件共享相同的 observable 实例

      export class MyServiceMock {
        private obs$ = of({// some object}).pipe(observeOn(asyncScheduler));
      
        doStuffAsync() {
          return obs$;
        }
      }
      

      在您的规范中,您可以等待它发出

      it('test someButtonClicked', async(async () => {
        component.someButtonClicked();
        const service = TestBed.get(MyService);
        await emitted(service.doStuffAsync());
        expect(component.someProperty).toBeDefined();
      }));
      

      看到您订阅了与组件相同的 observable 实例,您知道该组件已经运行了它的观察者功能,并且您在组件运行之后订阅了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-06-22
        • 1970-01-01
        • 1970-01-01
        • 2020-06-25
        • 1970-01-01
        • 2018-02-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多