Reputation: 1243
I am currently trying to test a function which calls a http request service and then does something in the subscribe part (calls a function and sets a variable). My approach so far was to just call the function and I thought the request-service will be called automatically and so the subscribe part will be executed. However, I feel like this is not the way of doing it as it's not working.
The function I want to test:
public trainBot() {
this.isTraining = true;
this.requestsService.trainModel(this.botId, false)
.subscribe(response => {
this.trainingStatus = this.trainingStatusMapping[response['status']];
this.pollTrainingStatus();
});
}
My test so far (which does not work).
it('should poll the training status', () => {
spyOn(component, 'pollTrainingStatus').and.callThrough();
component.trainBot();
fixture.detectChanges();
expect(component.pollTrainingStatus).toHaveBeenCalled();
});
So, can anyone tell me how to test that part inside the .subscribe(... part?
Update:
as someone suggested I added returnValue and async to my test. They are still not working, but looking like that now:
it('should poll the training status', fakeAsync(() => {
component.trainBot();
spyOn(service, 'trainModel').and.returnValue(Observable.of({'status': 'training'}));
spyOn(component, 'pollTrainingStatus').and.callThrough();
fixture.detectChanges();
tick(1);
expect(service.trainModel).toHaveBeenCalled();
expect(component.pollTrainingStatus).toHaveBeenCalled();
}));
The Error is the same
Upvotes: 3
Views: 8534
Reputation: 1427
Firstly, You need to create your Spies BEFORE you run the method trainBot(). That should fix your tests.
it('should poll the training status', () => {
spyOn(service, 'trainModel').and.returnValue(Observable.of({'status': 'training'}));
spyOn(component, 'pollTrainingStatus');
component.trainBot();
expect(service.trainModel).toHaveBeenCalled();
expect(component.pollTrainingStatus).toHaveBeenCalled();
}));
However, let's talk about your testing strategies.
To be honest, you're testing a component, not the service, so you shouldn't be letting the service method call through.
spyOn(service, 'trainModel').and.returnValue(Observable.of({'status': 'training'}));
Additionally, a unit test should test the smallest thing possible. You're actually testing the callback here, and the callback should probably be a named method instead of an anonymous arrow function. Then you can test and verify the callback's functionality in other tests.
public trainBot() {
this.isTraining = true;
this.requestsService.trainModel(this.botId, false)
.subscribe(response => this.onTrainbotSuccess(response));
}
public onTrainbotSuccess(response) {
this.trainingStatus = this.trainingStatusMapping[response['status']];
this.pollTrainingStatus();
}
In this test, you can test that the response method is getting called
it('should call service.trainModel', () => {
spyOn(service, 'trainModel').and.returnValue(Observable.of({'status': 'training'}));
component.trainBot();
expect(service.trainModel).toHaveBeenCalled();
});
it('should send success responses to onTrainbotSuccess()', () => {
spyOn(component, 'onTrainbotSuccess');
spyOn(service, 'trainModel').and.returnValue(Observable.of({'status': 'training'}));
component.trainBot();
expect(component.onTrainbotSuccess).toHaveBeenCalled();
});
Now we can write tests for specifically what the success callback does.
it('should poll the training status', () => {
spyOn(component, 'pollTrainingStatus');
component.onTrainbotSuccess({'status': 'training'});
expect(component.pollTrainingStatus).toHaveBeenCalled();
});
Upvotes: 9
Reputation: 415
The function passed to subscribe()
is called asynchronously. This means that your assertion is run before this.pollTrainingStatus()
is called. You need to use async()
or fakeAsync()
utility function.
In code can look like this (if using fakeAsync()
):
import { fakeAsync } from '@angular/core/testing';
it('should poll the training status', fakeAsync(() => {
...
expect(component.pollTrainingStatus).toHaveBeenCalled();
}));
Upvotes: 0