0
votes

This is my code in angular, the functionality is working all fine but the test cases are getting failed. Please tell me what i am doing wrong in the code? The error I am getting

HeadlessChrome 83.0.4103 (Windows 10.0.0) AboutComponent should create FAILED TypeError: Cannot read property 'getAboutInfo' of undefined at ** at AboutComponent.ngOnInit (http://localhost:9876/karma_webpack/src/app/about/about.component.ts:44:28) at callHook (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:3937:1) at callHooks (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:3901:1) at executeInitAndCheckHooks (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:3842:1) at refreshView (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:11795:1) at renderComponentOrTemplate (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:11903:1) at tickRootContext (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:13379:1) at detectChangesInRootView (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:13413:1) at RootViewRef.detectChanges (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:15093:22) at ComponentFixture._tick (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/testing.js:323:1)

import { async, ComponentFixture, TestBed} from '@angular/core/testing';
import { AboutComponent } from './about.component';
import { AboutService } from './about.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { Observable, of } from 'rxjs';
import { I18nService } from 'src/utils/i18n.service';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { AppModule } from './../app.module';

describe('AboutComponent', () => {
  let component: AboutComponent;
  let fixture: ComponentFixture<AboutComponent>;
  let dialogSpy: jasmine.Spy;
  let app: any;
  const mockDialogRef = {
    close: jasmine.createSpy('close')
  };
  let service: any;
  const data = '20/04/2019';
  let getAboutInfoSpy: any;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [AboutComponent],
      imports: [HttpClientTestingModule , AppModule],
      providers: [{ provide: AboutService, useValue: service },
        I18nService,
            { provide: MAT_DIALOG_DATA, useValue: {} },
            { provide: MatDialogRef, useValue: mockDialogRef}]
    }).compileComponents();
  }));

  beforeEach(async () => {
    fixture = TestBed.createComponent(AboutComponent);
    component = fixture.componentInstance;
    await fixture.whenStable();
    fixture.detectChanges();
  });

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

  it('infoList should be empty array', () => {
    expect(app['dataList'].length).toBe(0);
  });

  it('when OnInit invoked through service data will return to infoList ', async(() => {
    service = fixture.debugElement.injector.get(AboutService);
    spyOn(service, 'getAboutInfo').and.returnValue(of(data));
    app.ngOnInit();
    expect(app['dataList'].length).toBe(3);
  }));

  it('onCancel should close the dialog', async( () => {
    component.closePopup();
    expect(mockDialogRef.close).toHaveBeenCalled();
  }));

});
import { Component, OnInit, Inject } from '@angular/core';
import { AboutService } from './about.service';
import { Subscription } from 'rxjs';
import { MatDialogRef} from '@angular/material/dialog';
import { I18nService } from 'src/utils/i18n.service';

@Component({
  selector: 'app-about',
  templateUrl: './about.component.html',
  styleUrls: ['./about.component.scss']
})
export class AboutComponent implements OnInit {

  private aboutServiceSubscription: Subscription;
  dataList: any;
  locales: any = {};
  translator: any;
  
  constructor(
    private dialogRef: MatDialogRef<AboutComponent>,
    public aboutService: AboutService,
    private i18Service: I18nService) {}


  ngOnInit() {
    this.translator = this.i18Service.getTranslator();
    this.translator.translateObject.subscribe((item: any) => {
      this.locales = item;
    });
    this.aboutServiceSubscription =  this.aboutService.getAboutInfo().subscribe((data: any) => {
      if (data) {
        data = data.split('/');
        this.dataList = data;
      }
    });
  }


   /**
    * Closes the poup
    * @memberof AboutComponent
    */
  closePopup() {
    this.dialogRef.close();
  }
}
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AboutService {

  constructor(private http: HttpClient) {

   }

   getAboutInfo() {
    return this.http.get('/assets/aboutInfo.txt', {responseType: 'text'})
  }
}
1
providers: [{ provide: AboutService, useValue: service }, <= I don't see where you are instantiating a value for the service variable prior to the setupTaplar
how do i do that?sd_30
service = <something>. Such as service = jasmine.createObjectSpy('AboutService', ['getAboutInfo']). However, since the service is providedIn: 'root' you shouldn't have to do that at all. You should just be able to remove that providers line and later use TestBed.inject(AboutService) to get the service that was injected into your component.Taplar

1 Answers

0
votes

The error message indicates that you did not mock AboutService correctly, you passed an undefined to useValue, so the AboutService obtained through the Injector is undefined. You can't use spyOn to a undefined value.

Here is a working example, with irrelevant code removed:

about.component.ts:

import { Component, OnInit } from '@angular/core';
import { AboutService } from './about.service';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-about',
})
export class AboutComponent implements OnInit {
  private aboutServiceSubscription: Subscription;
  dataList: any;
  locales: any = {};
  translator: any;

  constructor(public aboutService: AboutService) {}

  ngOnInit() {
    this.aboutServiceSubscription = this.aboutService
      .getAboutInfo()
      .subscribe((data: any) => {
        if (data) {
          data = data.split('/');
          this.dataList = data;
        }
      });
  }
}

about.service.ts:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class AboutService {
  constructor(private http: HttpClient) {}

  getAboutInfo() {
    return this.http.get('/assets/aboutInfo.txt', { responseType: 'text' });
  }
}

about.component.spec.ts:

import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { AboutComponent } from './about.component';
import { AboutService } from './about.service';

fdescribe('62700708', () => {
  let component: AboutComponent;
  let fixture: ComponentFixture<AboutComponent>;
  let aboutServiceSpy: jasmine.SpyObj<AboutService>;
  const data = '20/04/2019';

  beforeEach(() => {
    aboutServiceSpy = jasmine.createSpyObj('AboutService', ['getAboutInfo']);
    aboutServiceSpy.getAboutInfo.and.returnValue(of(data));

    TestBed.configureTestingModule({
      declarations: [AboutComponent],
      imports: [HttpClientTestingModule],
      providers: [{ provide: AboutService, useValue: aboutServiceSpy }],
    }).compileComponents();
  });

  beforeEach(async () => {
    fixture = TestBed.createComponent(AboutComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

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

  it('when OnInit invoked through service data will return to infoList ', () => {
    expect(aboutServiceSpy.getAboutInfo).toHaveBeenCalledTimes(1);
    expect(component.dataList).toEqual(['20', '04', '2019']);
  });
});

unit test result:

enter image description here