ManBearPig
ManBearPig

Reputation: 93

Angular Unit Test if Component calls Http Service method : Error: <spyOn> : could not find an object to spy upon

My intention is to test if a Component Method calls a Service Method. The service method implementation is a Post, however I am not interested in the post mechanisms as I plan to test this in the service, rather I just want to know if the proper service method was called.

I am new to Unit testing however the error indicates the usage of spyon as follows Usage spyOn('object, methodName'), indicating that I am not properly creating an instance of the service object to test, however in order to create the service object it requires the HttpClient in the constructor, I have tried so many solutions and I have been reading for hours at this point, help would be greatly appreciate.

Best, SC

The Unit Test

describe('SchedulerComponent', () => {
  let component : SchedulerComponent;
  let fixture : ComponentFixture<SchedulerComponent>;
  let schedulerReportService : SchedulerReportService;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ SchedulerComponent ],
      imports: [BrowserAnimationsModule,
                MatSlideToggleModule,
                MatRadioModule,
                MatFormFieldModule,
                MatInputModule,
                MatSelectModule,
                MatNativeDateModule,
                MatDatepickerModule,
                NativeDateModule,
                NgxMaterialTimepickerModule,
                ReactiveFormsModule,
                FormsModule,
                HttpClientTestingModule],
      schemas:[CUSTOM_ELEMENTS_SCHEMA],
      providers: [{provide : SchedulerReportService}]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(SchedulerComponent);
    component = fixture.componentInstance;

    fixture.detectChanges();
  });

  fit('onSubmit should call scheduleReportService',inject([SchedulerReportService], (service: SchedulerReportService) => {

    let scheduleServiceSpy = spyOn(schedulerReportService, 'submitScheduledReport') ;
    let submitReportSpy = spyOn(component, 'onSubmit'); 

    submitReportSpy.and.callThrough();

    expect(scheduleServiceSpy).toHaveBeenCalled();


  }))

The corresponding service code is as follows

@Injectable({
  providedIn: 'root'
})
export class SchedulerReportService {

  constructor(private http: HttpClient) { }

   submitScheduledReport(servicerequest: ScheduleService) {

    console.log(servicerequest)

    // const headers = new HttpHeaders({'key':'value'});
    // return this.http.post<ScheduleService>(localUrl, servicerequest, {headers : headers} ).pipe(
    //   retry(1), catchError(this.handleError<ScheduleService>('scheduled report post error')));

  }

The component contained a method to trigger the service

export class SchedulerComponent implements OnInit {

  constructor(private fb: FormBuilder,
              private schedulerReportService: SchedulerReportService) { 
    onSubmit(){

    let report = this.schedulerForm.value;
    let scheduleServiceModel = new ScheduleService(report)
    this.schedulerReportService.submitScheduledReport(scheduleServiceModel);

  }
}

Upvotes: 1

Views: 1367

Answers (1)

alexortizl
alexortizl

Reputation: 2690

To specifically address the problem in your code above you need to set the service instance before calling spyOn. Something like this:

 schedulerReportService = fixture.debugElement.injector.get(SchedulerReportService);

  ...

 let scheduleServiceSpy = spyOn(schedulerReportService, submitScheduledReport');

However I recommend another way of doing this which is using a service spy stub. Since you don't need to test your service, only that a service method is called you don't need to inject the original SchedulerReportService instead you could provide a service object spy to Angular like this:

describe('SchedulerComponent', () => {
  let component : SchedulerComponent;
  let fixture : ComponentFixture<SchedulerComponent>;
  let schedulerReportService = jasmine.createSpyObj('SchedulerReportService', ['submitScheduledReport']);

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ SchedulerComponent ],
      imports: [BrowserAnimationsModule,
                MatSlideToggleModule,
                MatRadioModule,
                MatFormFieldModule,
                MatInputModule,
                MatSelectModule,
                MatNativeDateModule,
                MatDatepickerModule,
                NativeDateModule,
                NgxMaterialTimepickerModule,
                ReactiveFormsModule,
                FormsModule,
                HttpClientTestingModule],
      schemas:[CUSTOM_ELEMENTS_SCHEMA],
      providers: [{provide : SchedulerReportService, useValue: schedulerReportService}]
    })
    .compileComponents();

    fixture = TestBed.createComponent(SchedulerComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  }));

  it('onSubmit should call scheduleReportService', () => {
    component.onSubmit();
    expect(schedulerReportService.submitScheduledReport).toHaveBeenCalled();
  }));

This way you can test that schedulerReportService.submitScheduledReport was called without the overhead of the original service http calls.

Upvotes: 2

Related Questions