简体   繁体   English

在Angular 6组件ngOnInit函数中测试rxjs间隔

[英]Test rxjs interval in Angular 6 component ngOnInit function

I have a component with the following ngOnInit function which polls a service method for status updates:我有一个带有以下 ngOnInit 函数的组件,它轮询服务方法以进行状态更新:

ngOnInit() {
  interval(2000).pipe(
    switchMap(() => this.dataService.getStatus())
  ).subscribe((result) => {
    this.uploadStatus = result;
  );
}

I am trying to test that the updates are actually happening using the following code:我正在尝试使用以下代码测试更新是否实际发生:

beforeEach(() => {
  fixture = TestBed.createComponent(UploadComponent);
  component = fixture.componentInstance;
  fixture.detectChanges();
});


it('should start checking for status updates', fakeAsync(() => {
  const dataService = TestBed.get(DataService);
  // Mock the getStatus function
  spyOn(dataService, 'getStatus').and.returnValue(Observable.create().pipe(map(() => 'woo')));
  // Should not be initialised yet
  expect(component.uploadStatus).toBeUndefined();
  tick(2000);
  expect(component.uploadStatus).toBe('woo');
}));

However component.uploadStatus is always null.但是component.uploadStatus始终为 null。 How should I go about testing this type of scenario?我应该如何去测试这种类型的场景? Ideally I would like to check for multiple updates over time.理想情况下,我想随着时间的推移检查多个更新。

Thanks谢谢

What I eventually settled on was the following.我最终确定的是以下内容。 I also had to store the subscription so that I can cancel it at the end of the test to prevent "periodic timers still in queue" errors:我还必须存储订阅,以便在测试结束时取消订阅,以防止“定期计时器仍在队列中”错误:

ngOnInit() {
  // Store the subscription so we can unsubscribe when testing
  this.pollingSubscription = interval(2000).pipe(
    switchMap(() => this.dataService.getStatus())
  ).subscribe((result) => {
    this.uploadStatus = result;
  });
}

Then test as follows:然后测试如下:

it(`init`, fakeAsync(inject([DataService],
  (dataService: DataService) => {
    const testReturns = [
      of('woo'),
      of('yay')
    ];
    let currentReturn = -1;
    spyOn(dataService, 'getStatus').and.callFake(() => {
      currentReturn++;
      return testReturns[currentReturn];
    });

    expect(component.uploadStatus).toBeUndefined();
    fixture.detectChanges();
    // expect(component.uploadStatus).toBe('Login');
    // spyOn(dataService, 'getStatus').and.returnValue(of('woo'));
    component.ngOnInit();
    tick(2000);
    expect(component.uploadStatus).toBe('woo');
    tick(2000);
    expect(component.uploadStatus).toBe('yay');

    // Unsubscribe to prevent the "periodic timers still in queue" errors
    component.pollingSubscription.unsubscribe();
})));

The problem is in the beforeEach of the unit test template the Angular CLI set up for you.问题出在 Angular CLI 为您设置的beforeEach单元测试模板中。 You subscribe to the Observable created by interval during the first change detection cycle, ie, in ngOnInit .在第一个变更检测周期,即在ngOnInit ,您订阅了由interval创建的 Observable。

The subscription must take place inside the fakeAsync zone in order for tick to manage the Observable's time.订阅必须在fakeAsync区域内进行,以便tick管理 Observable 的时间。 Move the call to fixture.detectChanges inside the fakeAsync zone and you will see tick now manages the time.移动呼叫fixture.detectChanges内部fakeAsync区,你会看到tick现在管理的时间。

beforeEach((): void => {
  fixture = TestBed.createComponent(UploadComponent);
  component = fixture.componentInstance;
  // Get rid of call to detectChanges
});


it('should start checking for status updates', fakeAsync((): void => {
  // Arrange
  const dataService = TestBed.get(DataService);
  spyOn(dataService, 'getStatus').and.returnValue(of('woo'));
  
  // Assert
  expect(component.uploadStatus).toBeUndefined();

  // Act
  fixture.detectChanges();
  tick(2000);

  // Assert
  expect(component.uploadStatus).toBe('woo');

  // Clean up RxJS.interval function
  discardPeriodicTasks();
}));

you have to trigger change detection after tick, this should work您必须在勾选后触发更改检测,这应该可以工作

tick(2000);
fixture.detectChanges();
expect(component.uploadStatus).toBe('woo');

You may use setTimeout() to wait for 2 seconds (slightly more because you are again doing asynchronous task inside), and then check the property value.你可以使用setTimeout()等待2秒(稍微多一点,因为你又在里面做异步任务),然后检查property值。

Make sure to use done() method to notify the test runner that test case is completed.确保使用done()方法通知测试运行器测试用例已完成。

it('should start checking for status updates', (done) => {
  const dataService = TestBed.get(DataService);
  // Mock the getStatus function
  spyOn(dataService, 'getStatus').and.returnValue(Observable.create().pipe(map(() => 'woo')));
  // Should not be initialised yet
  expect(component.uploadStatus).toBeUndefined();

  setTimeout(()=> {
        expect(component.uploadStatus).toBe('woo');
        done();
  },2500);

});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM