Angular test case error: Cannot read property serviceUrl of undefined

1.2k Views Asked by At

MyService.ts file When we call the service from secp file serviceURl getting undefined.

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
    export class Sendnotificationservice {
      constructor(private http: HttpClient) {} 
      public sendnotification(
        notificationModel: SendnotificationToModel
      ): Observable<any> {
        return this.http.post<any>(
          AppConfig.settings.serviceUrl +
            'api/Sendnotificationservice/sendnotification',
          notificationModel
          //AppConfig.setting.serviceUrl getting cannot read property serviceurl of undefined
        );
      }
    }

Myservice.spec.ts Frome spec here we are calling the service when we call service we are not able to mock appconfig.ts return data

import { Injectable, Injector, OnInit } from "@angular/core";
import { HttpClient, HttpResponce } from "@angular/common/http";
import { IAppConfig } from "./app-config.model";
    describe('Sendnotificationservice', () => {
      let service: Sendnotificationservice;
      let httpSpy: HttpTestingController;
      beforeEach(() => {
        TestBed.configureTestingModule({
          imports: [HttpclientTestingModule],
          providers: [Sendnotificationservice],
        });
        service = TestBed.get(Sendnotificationservice);
        service = TestBed.get(HttpTestingController);
      });
      it('it should get mail', () => {
        const test = {
          clientno: '',
          firstName: 'dev',
          lastName: 'som',
          phoneNo: '484758373',
        };
        service.sendnotification(test).subscribe((data) => {
          expect(data).toEqual(false);
        });
      });
    });

AppConfig.ts Unable to mock return data to service test case file

import { Injectable, Injector, OnInit } from "@angular/core";
import { HttpClient, HttpResponce } from "@angular/common/http";
import { IAppConfig } from "./app-config.model";
import { environment } from "src/environments/environment";
@Injectable()
export class AppConfig {
  static settings: iAppConfig;
  constructor(private http:HttpClient) {}
  load() {
    const jsonFile =
      window.location.hostname.toLowerCase().indexof("localhost") !== -1
        ? 'assets/config/config.local.json'
        : 'assets/config/config.${environment.name}.json';
      return new Promise<any>((resolve, reject) => {
        this.http
          .get(jsonFile)
          .toPromise()
          .them((response: Response) => {
            AppConfig.settings = <any>response;
            resolve();
          })
          .catch((response: any) => {
            reject(
              'could not load file '${jsonFile}': ${JSON.stringify(response)}
            );
          });
      });
    }
  }
1

There are 1 best solutions below

0
Poul Kruijt On

That's not how you should mock in a unit tests. Now you have testing code inside your application code, and you should also test this testing code, and if you continue like that, you will end up in an infinite loop with testing test code to test testing code.

Better would be to also mock the AppConfig service:

import appConfig from 'assets/config/config.local.json';

class MockAppConfig {
  static settings: IAppConfig = appConfig as IAppConfig;
}

TestBed.configureTestingModule({
  imports: [HttpclientTestingModule],
  providers: [
    Sendnotificationservice, 
    { provide: AppConfig, useClass: MockAppConfig }
  ],
});

To make this work, you probably will have to set "resolveJsonModule": true inside your tsconfig.spec.json. You can also just export an object, instead of a .json file, considering you are most likely not serving this JSON file anymore. This will keep things type safe as well with code hinting.

Nevertheless, you are not there yet, because you can't test an Observable response like that. You will get an error saying that your "it has no expectations". There are multiple ways around this, a simple one would be call done:

it('it should get mail', (done) => {
  const test = {
    clientno: '',
    firstName: 'dev',
    lastName: 'som',
    phoneNo: '484758373',
   };

   service.sendnotification(test).subscribe((data) => {
     expect(data).toEqual(false);
     done(); // here
   });
});

This still is a bit weird to me though. You are not really testing the service object, but you seem to be testing the API response. This test should either be done on the backend side on which the API is called, or by using integration/e2e tests to assure proper working of the API