LoneWolf
LoneWolf

Reputation: 141

How to write a jasmine test case for a function which is called inside ngOnInit?

I am struggling to write a successful test case for a following code.

In component.ts

id = 123456;
data = [];
constructor(private serv: ApiService){}

ngOnInint(){
    getData(id);
}

getData(id){
   this.serv.getRequest(url+id).subscribe({
   (res){
        this.data = res;
   });
}

In spec.ts file

describe('component', () =>{
    let component: DataComponent,
    let fixture: ComponentFixture<OpenCasesComponent>;
    let serv: ApiService;

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            imports:[HttpClientTestingModule],
            declartions:[DataComponent],
            Providers: [ApiService]
        })
        .compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.CreateComponent(DataComponent);
        component = fixture.componentInstance;
        apiService = TestBed.inject(ApiService);
        fixture.detectChanges();
    });

    it('should make api call on load', fakeAsync(()=>{
        component.id = '123456';
        let fakeResponse = {
            name: 'John';
        }
        component.ngOnInit();
        fixture.detectChanges();
        
        spyOn(component, 'getData').withArgs('123456').and.callThrough();
        spyOn(apiService, 'getRequest')..and.returnValue(of(fakeResponse));
        fixture.detectChanges();
        tick();
        expect(component.getData).toHaveBeenCalled();
        expect(apiService.getRequest).toHaveBeenCalled();
        expect(component.data).toContain(fakeResponse);
    }));
}

Similar function call works with the similar code. Difference is, that func is triggered by a button click. My guess is I am not invoking the function here, if so How do I do it.

what am I doing wrong?

Upvotes: 0

Views: 387

Answers (1)

AliF50
AliF50

Reputation: 18859

The main thing you should realize is that the first fixture.detectChanges() you call is when ngOnInit is called.

I will help you, try to follow the comments:

import { of } from 'rxjs';
.....
describe('component', () =>{
    let component: DataComponent,
    let fixture: ComponentFixture<OpenCasesComponent>;
    // change the type of this line to be a spyObj
    let serv: jasmine.SpyObj<ApiService>;

    beforeEach(async () => {
        // create a spy object where we will mock ApiService
        // the first string argument is an identifier in case of errors
        // and the 2nd array of strings are public methods that you would like to mock
        serv = jasmine.createSpyObj<ApiService>('ApiService', ['getRequest']);
        await TestBed.configureTestingModule({
            // Pretty sure you don't need HttpClientTestingModule now
            // imports:[HttpClientTestingModule],
            // declarations is spelt wrong here
            declarations:[DataComponent],
            // lowercase p for providers here
            // when the component requires ApiService, give it the value
            // of this mock we just created
            providers: [{ provide: ApiService, useValue: serv }],
        })
        .compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.CreateComponent(DataComponent);
        component = fixture.componentInstance;
        apiService = TestBed.inject(ApiService);
        // this first fixture.detectChanges() we call is when ngOnInit is called
        // so let's make sure the API call will work before it is called
        // by providing fake data
        serve.getRequest.and.returnValue(of({ name: 'John' }));
        fixture.detectChanges();
    });

    it('should make api call on load', fakeAsync(()=>{
        component.id = '123456';
        let fakeResponse = {
            name: 'John';
        }
        // spy before calling ngOnInit
        spyOn(component, 'getData').and.callThrough();
        
        component.ngOnInit();
        fixture.detectChanges();
       
        expect(component.getData).toHaveBeenCalled();
        expect(serv.getRequest).toHaveBeenCalled();
        expect(component.data).toContain(fakeResponse);
    }));
}

If you don't want to make all of those changes, the following changes should suffice as well:

it('should make api call on load', fakeAsync(()=>{
        component.id = '123456';
        let fakeResponse = {
            name: 'John';
        }
        // set your spies before calling ngOnInit !!
        // try removing the withArgs here
        spyOn(component, 'getData').and.callThrough();
        spyOn(apiService, 'getRequest').and.returnValue(of(fakeResponse));
        component.ngOnInit();
        fixture.detectChanges();
        
        fixture.detectChanges();
        tick();
        expect(component.getData).toHaveBeenCalled();
        expect(apiService.getRequest).toHaveBeenCalled();
        expect(component.data).toContain(fakeResponse);
    }));

Upvotes: 1

Related Questions