I have a component with the following ngOnInit function which polls a service method for status updates:
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. 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. You subscribe to the Observable created by interval
during the first change detection cycle, ie, in ngOnInit
.
The subscription must take place inside the fakeAsync
zone in order for tick
to manage the Observable's time. Move the call to fixture.detectChanges
inside the fakeAsync
zone and you will see tick
now manages the time.
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.
Make sure to use done()
method to notify the test runner that test case is completed.
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);
});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.