Having trouble accessing the undefined property 'ngOnInit' and 'getData' in Angular while conducting unit tests with Jasmine

Looking to write a unit test for my component file that subscribes to a method in the service layer

Homecomponent.ts

import { Data } from './../model/data.model';
import { DataService } from './../services/data.service';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css'],
})
export class HomeComponent implements OnInit {
  data: Data[];
  constructor(private service: DataService) {}

  ngOnInit() {
    this.getData();
  }
  getData() {
    this.service.getData().subscribe(
      (data) => {
        console.log(data);
        this.data = data;
      },
      (err) => {
        console.log(err);
      },
    );
  }
}

HomeComponent.spec.ts

import { Data } from './../model/data.model';
import { HttpClientModule } from '@angular/common/http';
import { HomeComponent } from './home.component';
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
import { DataService } from '../services/data.service';
import { Observable } from 'rxjs';
import 'rxjs/add/observable/of';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { results } from '../model/home';

class MockMyService {
  public data: Data[];
  public getData(): Observable<Data[]> {
    this.data = results;
    return Observable.of(this.data);
  }
}

describe('HomeComponent', () => {
  let component: HomeComponent;
  let fixture: ComponentFixture<HomeComponent>;
  let mockSomeService: MockMyService;
  describe('Async', () => {
    beforeEach(async(() => {
      TestBed.configureTestingModule({
        declarations: [HomeComponent],
        providers: [
          {
            provide: DataService,
            useValue: mockSomeService,
          },
        ],
        imports: [HttpClientModule],
        schemas: [CUSTOM_ELEMENTS_SCHEMA],
      });
      TestBed.compileComponents();
      fixture = TestBed.createComponent(HomeComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
      mockSomeService = fixture.debugElement.injector.get(DataService);
      spyOn(mockSomeService, 'getData').and.returnValue(Observable.of(results));
      component.ngOnInit();
      fixture.detectChanges();
    }));
  });

  // afterEach(() => {
  //   fixture = undefined;
  // });

  it('method should be called', async () => {
    component.ngOnInit();
    mockSomeService.getData();
    expect(fixture.debugElement.componentInstance.data.length).toEqual(2);
  });
});

When I use component.ngOnInit(); it gives an error as

TypeError: Cannot read property 'ngOnInit' of undefined

When I'm not using component.ngOnInit() it gives me an error as

TypeError: Cannot read property 'getData' of undefined

How can I solve this issue? Appreciate any help in advance.

Answer №1

Almost there, but not quite!

  1. Make sure your TestBed configuration is not nested in a describe block that other describe blocks cannot access
  2. When using a mock service, use useClass: instead of useValue:
  3. No need to spyOn the service method when mocking out your service
  4. Instead of testing ngOnInit, test the getData method to ensure it sets the data attribute on your component correctly. This is because you can't be sure when ngOnInit completes running your asynchronous code
  5. Since getData is asynchronous, pass the done() callback into your it block to notify the test when asynchronous code should be completed

It looks like you may be using an older version of Angular, as your RXJS version is pre-version 6. Your code uses Observable.of() instead of the updated syntax, which is just of(). Consider upgrading!

Hope this explanation clarified things for you! :)

Here is the corrected test code:

class MockMyService {
  public data: Data[];
  public getData(): Observable<Data[]> {
    this.data = results;
    return Observable.of(this.data);
  }
}

describe('HomeComponent', () => {
  let component: HomeComponent;
  let fixture: ComponentFixture<HomeComponent>;
  let dataService: DataService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [HomeComponent],
      providers: [
        {
          provide: DataService,
          useClass: MockMyService
        }
      ],
      imports: [HttpClientModule],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
    }).compileComponents();
    fixture = TestBed.createComponent(HomeComponent);
    component = fixture.componentInstance;
    dataService = TestBed.get(DataService);
    fixture.detectChanges();
  });

  describe('Given the component is loaded', () => {
    describe('When getData returns mock data', () => {
      it('Then the data attribute has a length of 2', (done) => {
        dataService.getData().subscribe(() => {
          expect(component.data.length).toEqual(2);
          done();
        });
      });
    });
  });
});

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

Is it possible to deactivate the error message related to "Unable to ascertain the module for component..."?

I recently incorporated a new component into my TypeScript2+Angular2+Ionic2 project. Right now, I have chosen not to reference it anywhere in the project until it is fully developed. However, there seems to be an error thrown by Angular/ngc stating "Cannot ...

Issue with uncaught exceptions in promise rejection test

I am experiencing an issue with the following code: function foo(){ bar().catch(function(){ //do stuff } } function bar(){ return promiseReturner().then( function(result){ if(_.isEmpty(result)){ throw "Result is empty"; ...

Struggling with getting render props to work in Next.js version 13

Looking to develop a custom component for Contentful's next 13 live preview feature in the app directory, I thought of creating a client component that can accept a data prop and ensure type safety by allowing a generic type to be passed down. Here is ...

Retrieve a multitude of nested Records within the returnType that correlate with the number of arguments provided to the function

My goal is to dynamically define a deeply nested ReturnType for a function based on the number of arguments passed in the "rest" parameter. For example, if we have: getFormattedDates( dates: Date[], ...rest: string[] // ['AAA', 'BBB&apos ...

CORS policy preventing successful POST request

Every time I send a request to my API, Chrome blocks it due to CORS. To fix this issue on my Firebase Cloud Function, I included the line response.set('Access-Control-Allow-Origin','*'); (xxxxxx in the axios get hides the request URL) e ...

Implementing child components rendering in a React application using TypeScript

Just a little background information: I am attempting to build a carousel with pagination using ReactJS. Here is the code snippet I currently have: interface HTMLCarouselT { children: Array<JSX.Element> size: number; } const HTMLCarousel = ({ch ...

Is there a way to prevent the splash screen from appearing every time I navigate using a navbar link in a single page application (SPA)?

Recently, I came across this tutorial and followed it diligently. Everything seemed to be working perfectly until I encountered an issue with my navbar links. Each time I clicked on a link, the splash screen appeared, refreshing the page without directing ...

Displaying a specific item with an icon in a list for user interaction in Ionic 3

Currently, I am working on a brand new music application. As part of my project, I have curated an extensive list of songs displayed on the homepage. My objective is to implement a feature where when a user clicks on a specific song from the list, it will ...

Guide to limiting interceptor scope to a specific module in Angular

Below is the angular interceptor used to replace the auth token: @Injectable() export class TokenInterceptor implements HttpInterceptor { constructor( ) { } intercept( request: HttpRequest<any>, next: HttpHandler ): Observable<H ...

Encountered an issue when attempting to post to an ASP.NET Core Web API utilizing Windows authentication

The setup consists of an AspNetCore WebApi using default configuration for Windows authentication and CORS enabled. The client side utilizes Angular with both GET and POST methods implemented. Successfully executing the GET call: this.http.get("https://l ...

Using Angular2's *ngIf directive to conditionally display content based on the length of

After referring to https://angular.io/docs/ts/latest/guide/displaying-data.html and a stack post on how to check for an empty object in an angular 2 template using *ngIf, I am still encountering a syntax error stating "self context undefined". If I remove ...

Enhance your app with the seamless navigation experience using Ionic 2

As a beginner in Angular2, Ionic2, NodeJS ... I am experimenting with writing some code to learn. In my journey, I attempted to create a screen with 3 tabs and a menuToggle. When the application is launched, clicking the menuToggle button on the first tab ...

Why do I keep getting errors in TypeScript when I manipulate DOM elements using getElementsByClassName(), even though my application still functions properly?

I am dealing with an Angular2 application. Unfortunately, I have had to resort to using the following code within a component method (I know it's not ideal, but...): let confirmWindowDOM = document.getElementsByClassName('modal')[0]; confir ...

Managing the closest element depending on the selected option in Angular 5

My task involves accessing the nearest element of a dropdown. The HTML below includes multiple dropdowns and input boxes. When selecting options from the dropdown, I need to enable or disable the input box closest to it. <div class="form-block" *ngFor= ...

Exploring ngTemplateOutlet and ngtemplate in complex nested forms with Angular

I am currently working on generating reactive forms from JSON data. My goal is to create a formGroup and its nested forms based on the provided data, and automatically generate an HTML form using templates. I have outlined the steps I took with sample data ...

Angular encountering a 405 Method not allowed error along with the "Provisional Headers are shown" message

It's really frustrating me. I'm attempting to make a simple request to my api server by adding the header, but it keeps showing me the message "Provisional Headers are shown" and then fails on the subsequent request. Provisional headers are sho ...

Angular Module without any components

The Angular modules documentation provides an example of a module that includes a template, a component, and a provider. This module is then imported into the root module and instantiated by using the element defined by the component like this: <app-co ...

The offsetWidth of the nativeElement in Angular 2's ElementRef is consistently returning a value

Is there a way to retrieve the width of an element with a dynamic width using Angular 2? I can easily accomplish this with pure javascript, but not through Angular 2. constructor(private element: ElementRef) { // .... Obtaining the width let width = thi ...

``When the checkbox component is clicked in a table, it shifts position

I can't seem to get the Angular Material checkbox component to function properly. When I try clicking on the checkbox, it ends up moving up, displaying only half of the checkbox. Is there anyone who knows how to fix this issue? <table class="mate ...

When accessing an Angular 7 page directly through the URL in the browser, it becomes unresponsive. However, the page works perfectly fine when navigating

I've been tackling a poorly developed Angular 7 legacy application and encountering a bizarre issue. There's a component that requires a parameter for email verification, but when the URL is visited directly, it doesn't function as expected. ...