Kateryna
Kateryna

Reputation: 45

Testing a component that depends on 2 services

I have a component that uses a service to retrieve information. But that service also gets configs from Configuration Service from a static variable conf. When running karma tests the const variable is undefined.

I am aware that I can create mock service, however shall I create 2 services to test this component? And if yes, I have other services that also use Configuration Service, so I have to create mock service for each of them? Seems like lots of work, but I haven't found a better solution :( I provided both ConfigurationService and the Service I am using if that that makes any difference.

TypeError: Cannot read property 'apiUrl' of undefined

apiUrl is a property of conf that is a static variable in ConfigurationService.

ConfigService.ts

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

import * as YAML from 'js-yaml';
import {Config} from './models/Config';


@Injectable()
export class ConfigService {
  public static  conf: Config;

  constructor(private http: HttpClient) {}
   async load() {
       const res = await this.http.get<Config>('assets/config.yml', {responseType: 'text' as 'json'}).toPromise();
       ConfigService.conf = YAML.load(res).environment;
  }
}

InfoService.ts

export class InfoService {
  private InfoUrl = ConfigService.conf.apiUrl + '/info';

  constructor(private http: HttpClient) {}
  getInfo(){
    return http.get(InfoUrl);
  }
}

InfoComponent.ts

export class InfoComponent implements OnInit {
  private info;
  constructor(private infoService: InfoService) {}

  ngOnInit() {}

  loadInfo() {
    this.info = this.infoService.getInfo();
  }

InfoComponent.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { InfoComponent } from './info.component';
import {HttpClientModule} from '@angular/common/http';
import {InfoService} from './info.service';
import {ConfigService} from '../shared/config.service';


describe('InfoComponent', () => {
  let component: InfoComponent;
  let fixture: ComponentFixture<InfoComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientModule],
      declarations: [InfoComponent],
      providers: [
          ConfigService
          InfoService,
      ],
    })
    .compileComponents();
  }));

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

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

Upvotes: 1

Views: 810

Answers (1)

Shashank Vivek
Shashank Vivek

Reputation: 17494

Basically your component needs InfoService. The core concept of Unit testing revolves around isolating the target code and test it. So, in your case, you dont need to create dependency on ConfigService. There should be a separate Unit test to test the behavior of ConfigService

class InfoServiceStub {
  getInfo(){
    return of({
        /// your mock data
     });
  }
}


describe('InfoComponent', () => {
  let component: InfoComponent;
  let fixture: ComponentFixture<InfoComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientModule],
      declarations: [InfoComponent],
      providers: [
          {provide: InfoService, useClass: InfoServiceStub },
      ],
    })
    .compileComponents();
  }));

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

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

Upvotes: 1

Related Questions