Tips for creating a jasmine test scenario for a function executed within the ngOnInit lifecycle hook

I am finding it challenging to create a successful test case for the code snippet below.

In my component.ts file

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

ngOnInint(){
    getData(id);
}

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

In my 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);
    }));
}

A similar function call works with code somewhat alike. The only difference is that the function is triggered by a button click. I suspect I am not invoking the function here, if so How do I do it.

Can someone point out what I might be doing incorrectly?

Answer №1

One important thing to note is that the initial fixture.detectChanges() is when the ngOnInit function gets executed.

Let me guide you through the process with these comments:

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

    beforeEach(async () => {
        // Create a spy object to mock ApiService
        // The first string argument acts as an identifier in case of errors
        // The second array of strings contains public methods to mock
        serv = jasmine.createSpyObj<ApiService>('ApiService', ['getRequest']);
        await TestBed.configureTestingModule({
            // HttpClientTestingModule is no longer necessary
            // imports:[HttpClientTestingModule],
            // 'declarations' is misspelled here
            declarations:[DataComponent],
            // 'providers' should be lowercase here
            // Provide the mock we just created when the component requires ApiService
            providers: [{ provide: ApiService, useValue: serv }],
        })
        .compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.CreateComponent(DataComponent);
        component = fixture.componentInstance;
        apiService = TestBed.inject(ApiService);
        // The first fixture.detectChanges() call triggers the ngOnInit function
        // Ensure the API call works before it is made by providing fake data
        serv.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 prefer not to make all those modifications, the following changes should be enough:

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);
    }));

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Navigating a table while keeping headers in place at the top

Trying to construct a table where the thead remains fixed while the tbody scrolls. Utilizing percentages and fixed width for cell size determination, aiming for uniformity and alignment between percentage td's and thead headers. Referenced JSFiddle d ...

jQuery Validation is not functioning correctly

I am facing an issue with implementing jQuery.Validation. I have written a script and included all JS files below, but for some unknown reason, the validation always returns that the form is valid. Below is the JavaScript code I am using: $(document).rea ...

Remove a comment from the page without needing to refresh the entire

Is there a way to enhance this code so that when a comment is deleted, the page does not refresh? It can be frustrating when deleting a comment causes the page to scroll back to the top. AJAX function deleteComment(pid){ $.ajax({ type: "PO ...

Validating HTML forms using Javascript without displaying innerHTML feedback

Hey, I'm just diving into web development and I'm struggling to figure out why my innerHTML content isn't displaying on the page. Can anyone offer some assistance? I'm trying to display error messages if certain fields are left empty, b ...

Sending an associative array to Javascript via Ajax

Learning a new programming language is always a great challenge. Can someone guide me on how to efficiently pass an associative array to JavaScript using AJAX? Below is a snippet of code from server.php: $sql = "SELECT Lng, Lat, URL FROM results LIMIT ...

Is your Typescript compilation running into a problem with jwt tokens?

Having issues while trying to compile a typescript file as the compiler is throwing an error message: Error: TS2339 - The property 'payload' does not exist on type 'string | object'. Property 'payload' does not exist on type ...

Transferring Data between Rails and Angularjs using JSON

Utilizing Angularjs to fetch JSON data from a Rails test app deployed on Heroku is proving to be a bit challenging. Below you can find the snippets of my Angular and Rails code. An error message displayed in my Firebug console reads: "NetworkError: 404 N ...

Getting the error message ""Cannot return null for non-nullable field" while trying to subscribe in NestJS using GraphQL"

I have developed a Node.js backend using Nestjs and incorporating Graphql, with my frontend built on Ionic/Angular utilizing Apollo-angular for graphql functionalities. However, I am encountering difficulties in subscribing to data additions or changes. St ...

What sets apart the states of the select tag from other input tags in Angular?

I am having trouble displaying an error message for a select tag when it is in a touched state. The error handling seems to be working fine for other input tags, but not for the select tag. Below is the code snippet: <div class="form-g ...

Types are not appearing in @types/node

I have added @types/node to my project. In the index.ts file, the default node modules are being treated as type any. const fs = require('fs'); The type of fs is currently set to any. { "ts-node": { "cwd": "/User ...

Retrieving information from a function beyond the scope of Mongoose

I am currently facing an issue with my function in which I need to return the Mongoose query data so that I can use it. However, I am only able to access the sum within the .exec function. How can I manage to retrieve the sum value outside of this functi ...

Proper method for adding elements in d3.js

I have a block of code that selects an #id and appends a svg-element into it: var graph = d3.select(elemId).append("svg") .attr('width', '100%') .attr('height', '100%') .append('g') Within th ...

Is there a method to adjust the styling of my <header> once the page hits a specific #anchor point?

Seeking some assistance with a website design challenge I am facing. I am currently working on a one-page portfolio site with four #anchor points that serve as "pages". Each div has a height of 100% to fill the entire browser height. The header, which co ...

Ways to display an error notification alongside another message

I have set up a validation directive that requires users to check a box. If the checkbox is left unchecked, an error message will be displayed. However, I am facing an issue where the message overlaps with the checkbox text. https://i.sstatic.net/iTKoo.jp ...

Can you determine if a function is a generator function if .bind() has already been applied to it?

It seems like using .bind(this) on a generator function is interfering with determining if the function is actually a generator. Any suggestions on how to resolve this issue? function checkIfGenerator(fn) { if(!fn) { return false; } ...

differences in opinion between JavaScript code and browser add-ons/extensions

As the owner and developer of an e-commerce website, I find that potential customers often contact us regarding ordering issues. Upon investigation, we consistently discover that the problem is caused by JavaScript errors. We frequently check their browse ...

Strategies for extracting data from a third-party website that utilizes JavaScript to set the value

Before, I would use jQuery to load external website content such as html or json. Sometimes, I even utilized a proxy PHP page in order to bypass strict origin policies on certain sites. However, I've encountered an issue with some websites. In the HT ...

Ways to make the input field appear invalid without the use of forms, causing the bottom outline to turn red when a specific condition is met

Currently, the application does not utilize any forms. I am interested in making the input field invalid under certain conditions within the component. For example, if a user leaves the input field blank, I would like the bottom outline to turn red when an ...

Error Occurs: 'PRM_MissingPanel' randomly in Ajax Update Panel

When using the Ajax Update Panel in Visual Studio to handle post backs for my search function, I encounter an issue. After generating a gridview with the results (i.e., searching for a member), whenever I update to the newest version from TFS, an error is ...

Ways to Deduct 10 Days from a Date using JavaScript

After receiving some helpful suggestions, I managed to solve my date issue by using moment.js. Here is the snippet of code where I implemented moment.js: var preorderdate = moment(date).subtract('days',10).format('MMMM D, YYYY'); var r ...