简体   繁体   中英

Angular2 - Test a function that calls a http service inside

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

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();
});

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();
}));

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.

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