Search code examples
angularjasminekarma-jasmineangular15

Error: Expected spy get to have been called


The following Unit Tests:

    const mockActiveService1 = {
      .......
    }
    const mockNotActiveService1 = {
      .......
    }
    
    describe('Component1 ', () => {
      let component: Component1 ;
      let fixture: ComponentFixture<Component1>;
    
      beforeEach(async () => {
        TestBed.configureTestingModule({
          declarations: [Component1],
          providers: [
            { provide: Service1, useValue: mockActiveService1 }
          ],
          imports: [HttpClientTestingModule, ToastrModule.forRoot()]
        }).compileComponents();
      });
    
      beforeEach(() => {
        fixture = TestBed.createComponent(Component1);
        component = fixture.componentInstance;
        service1 = TestBeb.inject(Service1);
      });
    
    
  it('should load the current dataService2', async () => {
    spyOn(service1, 'get').and.returnValue(of(mockActiveService1));
    
    fixture.detectChanges();

    await component.ngOnInit();

    expect(service1.get).toHaveBeenCalled();
    expect(component.dataService2).toEqual(mockActiveService1);
    expect(component.showCountdown).toBeFalse();
    expect(component.loading).toBeFalse();
  });

  it('should show info message and navigate to default route when there is no active dataService2', fakeAsync (() => {
    const toastrServiceSpy = spyOn(toastrServiceMock, 'info');
    const routerSpy = spyOn(routerMock, 'navigate');
  
    const getSpy = spyOn(service1, 'get').and.callThrough().and.returnValue(of(mockNotActiveService1));
  
    fixture.detectChanges();
    component.ngOnInit();
    tick();
  
    expect(getSpy).toHaveBeenCalled();
    expect(toastrServiceSpy).toHaveBeenCalledWith('no active dataService2', 'Info!');
    expect(routerSpy).toHaveBeenCalledWith(['/default']);
  }));

Both tests are returning the same error:

Error: Expected spy get to have been called.
    at <Jasmine>
    at UserContext.apply (src/app/features/home/home.component.spec.ts:70:37)
    at _ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:409:30)
    at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/zone-testing.js:303:43)

I cannot figure the reason out. I tried using different format to build the spy, but nothing looks work. Any clue to help me? The code of the component must call the get method as far as it is:

ngOnInit(): void {
        this.loading = true;
        this.showCountdown = false;
        this.loadCurrentdataService2();
    }
loadCurrentdataService2(): void {
        this.service2.get(0).subscribe(results => {
            if (results) {
                this.dataService2= results;

                if (this.dataService2 && this.dataService2.isActive) {
...............

I have checked the code and the service2.get(0) is being called properly. But if I added the next console.log to the unit test:

it('should load the current dataService2', fakeAsync(() => {
    spyOn(service1, 'get').and.callThrough();

    fixture.detectChanges();

    component.ngOnInit();
    tick();

    console.log('service1.get.calls', (service1.get as any).calls);

    expect(service1.get).toHaveBeenCalled();
    expect(component.dataService2).toEqual(mockActiveService1);
    expect(component.showCountdown).toBeFalse();
    expect(component.loading).toBeFalse();
  }));

I receive:

LOG: 'service1.get.calls', CallTracker{track: function(context) { ... }, any: function() { ... }, count: function() { ... }, argsFor: function(index) { ... }, thisFor: function(index) { ... }, all: function() { ... }, allArgs: function() { ... }, first: function() { ... }, mostRecent: function() { ... }, reset: function() { ... }, saveArgumentsByValue: function() { ... }}

I tried as well:

spyOn(service1, 'get').and.callFake(() => {
return of(mockActiveService1);
});

I can´t figure it out why get method is not being called in the unit test. Please, any help or hint will be really appreciated.


Solution

  • Finally I found the answer. The problem was the service of the component wasn´t the same than the service injected in the test. To solve this, I force to the component´s service to be the same injecting it in the test by the constructor:

    it('should load the current dataService2', fakeAsync(inject([Service1], (service1: Service1) => {
          spyOn(service1, 'get').and.callFake(() => {
            return of(mockService1);
          });
          sanitazer.bypassSecurityTrustUrl.and.callFake(input => input);
          sanitazer.bypassSecurityTrustStyle.and.callFake(input => input);
          sanitazer.bypassSecurityTrustHtml.and.callFake(input => input);
    
          component = new HomeComponent(sanitazer, service1, router, toastrService);
    
      component.ngOnInit();
      tick(1000);
      fixture.detectChanges();
    
      expect(service1.get).toHaveBeenCalledWith(0);
      expect(component.campaign).toEqual(mockService1);
      expect(sanitazer.bypassSecurityTrustUrl).toHaveBeenCalledTimes(1);
      expect(sanitazer.bypassSecurityTrustHtml).toHaveBeenCalledTimes(10);
      expect(sanitazer.bypassSecurityTrustStyle).toHaveBeenCalledTimes(5);
      expect(component.showCountdown).toBeTrue();
      expect(component.loading).toBeFalse();
    })));