angularunit-testingkarma-runnerkarma-mocha

Karma - NullInjectorError: No provider for HttpClient


I get NullInjectorError: No provider for HttpClient when i run my test.

Using Angular 8 with Karma. I followed examples and am new to Karma. So not sure why this is happening. Why?

Most of what i read says to include the httpclienttesting module. But that does not seem to make a difference.

enter image description here

My getHelpfulLinks component-

 import { Component, OnInit } from '@angular/core';
 import { TkDataService } from '../tk-services/tk-data/tk-data.service';

 @Component({
   selector: 'app-tk-helpful-links',
   templateUrl: './tk-helpful-links.component.html',
   styleUrls: ['./tk-helpful-links.component.scss']
 })
 export class HelpfulLinksComponent implements OnInit {
    public res: {};

   getHelpfulLinks(): any {

     return this.dataSvc.get('gethelpfulLinks', {})
     .subscribe(res =>  {
      this.res = res;
   });

   }

 constructor(
   private dataSvc: TkDataService
   ) {
 }

    ngOnInit() {
     this.getHelpfulLinks();

   }
    }
 }

And my .spec file-

 import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
 import { HelpfulLinksComponent } from './tk-helpful-links.component';
 import { TkDataService } from '../tk-services/tk-data/tk-data.service';
 import { HttpClient} from '@angular/common/http';
 import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
 import {HttpClientModule} from '@angular/common/http';

  describe('HelpfulLinksComponent', async() => {
   let component: HelpfulLinksComponent;
   let http: HttpClient;
   let mockTkDataService = new TkDataService(http);
   let httpTestingController: HttpTestingController;
   let fixture;

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

     TestBed.configureTestingModule({
       declarations: [ HelpfulLinksComponent ],
       imports: [ HttpClientTestingModule ],
     })
    .overrideComponent(HelpfulLinksComponent, {
       set: {
         providers: [
           { provide: TkDataService, useValue: mockTkDataService }
         ]
       }
     })
     .compileComponents();

     httpTestingController = TestBed.get(HttpTestingController);
     component = TestBed.get(mockTkDataService);

     afterEach(() => {
       httpTestingController.verify();
     });
   });
   it('HelpfulLinksComponent should be created',  () => {
     expect(component).toBeTruthy();
   });
 });

My TKDataService file (partial)-

 export class TkDataService {
constructor(
    private http: HttpClient
    ) {

    }

get(route: string, data: any, responseType?): Observable<any[]>{
    const observe = responseType === 'blob' ? 'response' : 'body';
    let requestHeaders = new HttpHeaders();
         requestHeaders = requestHeaders.set('Authorization', localStorage.getItem('bearerToken'));
    // tslint:disable-next-line: max-line-length
    return this.http.get<any[]>(environment.baseAPIUrl + route,
       {withCredentials: false, headers: requestHeaders, responseType, observe: observe as 'body'} );

}
}

Solution

  • Short Answer:

    You are passing a null httpClient to mockTkDataService in these line:

    let http: HttpClient;
    let mockTkDataService = new TkDataService(http);
    

    What you should do:

    You don't need to mock TkDataService at all. The HttpClientTestingModule will resolve the httpClient inside it.

    Just add the original TkDataService to the providers array in the test bed configuration like this:

    TestBed.configureTestingModule({
           declarations: [ HelpfulLinksComponent ],
           imports: [ HttpClientTestingModule ],
           providers: [ TkDataService ]
         })
    

    and use it later like this:

    const tkDataService = getTestBed().get(TkDataService)
    
    
    httpService.get<any>(dummyUrl, dummyTelemetryAction).subscribe(
                (response: HttpResponse<any>) => {
                    expect(response).toBeTruthy();
    }
    
    const req1 = httpMock.expectOne(dummyUrl);
    
    req1.flush(null, { status: 200, statusText: 'ok' });