Search code examples
angularangular-test

TypeError: Cannot read properties of undefined (reading 'fetchPersonnelList')


I am getting an error with my test spec:

TypeError: Cannot read properties of undefined (reading 'fetchPersonnelList')

Here is my full code:

    import { HttpClientModule } from '@angular/common/http';
import { Component } from '@angular/core';
import {
    async,
    ComponentFixture,
    fakeAsync,
    flush,
    TestBed,
    tick,
} from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ActivatedRoute, Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { of } from 'rxjs';
import { SharedModule } from '../../../../../shared/shared.module';
import { PersonnelDataService } from '../services/personnel-data.service';
import { testHFSTaleSchema } from './hfs-table.schema';
import { ListPersonnelComponent } from './list-personnel.component';

const mockPersonnelDataService = {
    list$: of(testHFSTaleSchema.rows),
    fetchPersonnelList: function () {
        return { subscribe: () => this.list$ };
    },
    setPageSize: function () {
        return this.list$;
    },
    fetchPaginatedList: () => {},
};

@Component({
    selector: 'app-details-personnel',
    template: '',
})
export class PersonnelDetailsComponent {}

fdescribe('ListPersonnelComponent', () => {
    let component: ListPersonnelComponent;
    let fixture: ComponentFixture<ListPersonnelComponent>;
    let activatedRoute = { url: {} } as ActivatedRoute;
    let service: PersonnelDataService;
    let router;
    const routes = [
        {
            path: 'personnel-details',
            component: PersonnelDetailsComponent,
        },
    ];

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [
                RouterTestingModule.withRoutes(routes),
                SharedModule,
                BrowserAnimationsModule,
                HttpClientModule,
            ],
            declarations: [ListPersonnelComponent],
            providers: [
                {
                    provide: PersonnelDataService,
                    useValue: mockPersonnelDataService,
                },
                { provide: ActivatedRoute, useValue: activatedRoute },
            ],
        }).compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(ListPersonnelComponent);
        component = fixture.componentInstance;
        router = TestBed.inject(Router);
        fixture.detectChanges();
    });

    it('should create', () => {
        expect(component).toBeTruthy();
    });

    it('should call fetchPersonnelList on page Oninit', fakeAsync(() => {
        component.ngOnInit();
        tick(1000);
        expect(service.fetchPersonnelList).toHaveBeenCalled();//error
        flush();
    }));
});

Update

As per forestG suggestion I update the code by injecting service. When mock service existing, still I am getting an error as :

Error: <toHaveBeenCalled> : Expected a spy, but got Function.

When mock service added, why I am getting this error?

Suggestion update

I have updated my code like:

it('should call fetchPersonnelList on page Oninit', fakeAsync(() => {
        const fetchPersonnelListSpy = spyOn(
            service,
            'fetchPersonnelList'
        ).and.returnValue(of([]));
        component.ngOnInit();
        tick(1000);
        expect(fetchPersonnelListSpy).toHaveBeenCalled(); 
        flush();
    }));

it works. But what is the benefit of my mock service? How can I expect my mock data? I can put my data directly in the retunValue part, right?


Solution

  • You should be able to directly spy on the service spyOn(component.service, 'fetchPersonnelList') and use .callThrough() which will call the mock function, if you want to prevent calling the mock function and pass some other value then use .returnValue(of([]));

    it('should call fetchPersonnelList on page Oninit', () => {
      const fetchPersonnelListSpy = spyOn(component.service, 'fetchPersonnelList').and.callThrough();
      component.ngOnInit();
      expect(fetchPersonnelListSpy).toHaveBeenCalled();
    });