angularkarma-jasmineangular-unit-test

spyOn doesn't seem to work in angular unit test, actual code invoked


I have following test. It fails and appears that the spyOn is not mocking the service correctly or may be I don't understand it. I put a console in the component method and could see it when test runs, shouldn't the actual method be not called when I am spying on it? I thought may be it's due to api taking sometime, so tried fakeAsync, flush and tick etc. but none worked.

describe('AppComponent', () => {
  let service: any;
  let fixture: ComponentFixture<AppComponent>;
  let component: AppComponent;
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [RouterTestingModule, HttpClientTestingModule],
      declarations: [AppComponent],
      providers: [PatientdataService],
    }).compileComponents();

    service = TestBed.inject(PatientdataService);
    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
  });

  it('should call getPatient from service', () => {      

    const dataSpy = spyOn(component, 'loadPatientData');
    const patientSpy = spyOn(service, 'getPatientValues').and.returnValue(of([]));
    const medSpy = spyOn(service, 'getMedicationValues').and.returnValue(of([]));

    component.getPatientDetails();

    expect(dataSpy).toHaveBeenCalledTimes(1);
    expect(patientSpy).toHaveBeenCalledTimes(1);
    expect(medSpy).toHaveBeenCalledTimes(1);
  }); 
});

Following is the component method it's trying to test -

getPatientDetails() {
    this.service.getPatientValues().subscribe({
      next: (data) => {
        this.loadPatientData(JSON.parse(data));    
        this.service.getMedicationValues().subscribe({
          next: (data) => {
            this.loadMedicationData(JSON.parse(data));    
            this.service.getConditionsValues().subscribe({
              next: (data) => {
              ...
              ...

This is the error I get -

Error: Expected spy loadPatientData to have been called once. It was called 0 times.
            at <Jasmine>
            at UserContext.<anonymous> (src/app/app.component.spec.ts:41:21)
            at _ZoneDelegate.invoke (node_modules/zone.js/fesm2015/zone.js:372:1)
            at ProxyZoneSpec.onInvoke (node_modules/zone.js/fesm2015/zone-testing.js:287:1)
        Error: Expected spy getMedicationValues to have been called once. It was called 0 times.
            at <Jasmine>
            at UserContext.<anonymous> (src/app/app.component.spec.ts:43:20)
            at _ZoneDelegate.invoke (node_modules/zone.js/fesm2015/zone.js:372:1)
            at ProxyZoneSpec.onInvoke (node_modules/zone.js/fesm2015/zone-testing.js:287:1)

Solution

  • If you try to parse JSON that isn't valid you'll get runtime exception Unexpected end of JSON input, I suspect that's what's happening here

    // You're returning of([])
    // So data is [], which isn't valid 
    // for JSON.parse
    this.loadPatientData(JSON.parse(data))
    

    If this is how your code should behave, and isn't a mistake, then try using of(JSON.stringify([]))

    The reason the code is invoked is because you use returnValue for getPatientValues - if you don't use that then the function won't be invoked - but none of the other functions would be called after that either