Angular 2 Error: No provider for Http in Karma-Jasmine Test

Chris picture Chris · Oct 12, 2016 · Viewed 41.6k times · Source

I keep getting the following error in my karma test even though my app is working perfectly with no errors. It is saying that there is no provider for Http. I'm using import { HttpModule } from '@angular/http'; in my app.module.ts file and adding it to the imports array. The karma error looks like the following:

Chrome 52.0.2743 (Mac OS X 10.12.0) App: TrackBudget should create the app FAILED
    Failed: Error in ./AppComponent class AppComponent_Host - inline template:0:0 caused by: No provider for Http!
    Error: No provider for Http!
        at NoProviderError.Error (native)
        at NoProviderError.BaseError [as constructor] (webpack:/Users/ChrisGaona%201/budget-tracking/~/@angular/core/src/facade/errors.js:24:0 <- src/test.ts:2559:34)
        at NoProviderError.AbstractProviderError [as constructor] (webpack:/Users/ChrisGaona%201/budget-tracking/~/@angular/core/src/di/reflective_errors.js:42:0 <- src/test.ts:15415:16)
        at new NoProviderError (webpack:/Users/ChrisGaona%201/budget-tracking/~/@angular/core/src/di/reflective_errors.js:73:0 <- src/test.ts:15446:16)
        at ReflectiveInjector_._throwOrNull (webpack:/Users/ChrisGaona%201/budget-tracking/~/@angular/core/src/di/reflective_injector.js:761:0 <- src/test.ts:26066:19)
        at ReflectiveInjector_._getByKeyDefault (webpack:/Users/ChrisGaona%201/budget-tracking/~/@angular/core/src/di/reflective_injector.js:789:0 <- src/test.ts:26094:25)
        at ReflectiveInjector_._getByKey (webpack:/Users/ChrisGaona%201/budget-tracking/~/@angular/core/src/di/reflective_injector.js:752:0 <- src/test.ts:26057:25)
        at ReflectiveInjector_.get (webpack:/Users/ChrisGaona%201/budget-tracking/~/@angular/core/src/di/reflective_injector.js:561:0 <- src/test.ts:25866:21)
        at TestBed.get (webpack:/Users/ChrisGaona%201/budget-tracking/~/@angular/core/bundles/core-testing.umd.js:1115:0 <- src/test.ts:5626:67)
Chrome 52.0.2743 (Mac OS X 10.12.0): Executed 1 of 1 (1 FAILED) ERROR (0.229 secs / 0.174 secs)

Here is my app.component.ts file:

import {Component} from '@angular/core';
import {Budget} from "./budget";
import {BudgetService} from "./budget.service";

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
    providers: [BudgetService]
})
export class AppComponent {
    title = 'Budget Tracker';

    budgets: Budget[];
    selectedBudget: Budget;

    constructor(private budgetService: BudgetService) { }

    ngOnInit(): void {
        this.budgetService.getBudgets()
            .subscribe(data => {
                this.budgets = data;
                console.log(data);
                this.selectedBudget = data[0];
                console.log(data[0]);
            });
    }
}

Here is my simple spec:

import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';

describe('App: TrackBudget', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
        declarations: [
            AppComponent
        ]
    });
  });

  it('should create the app', async(() => {
    let fixture = TestBed.createComponent(AppComponent);
    let app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  }));
});

The error seems to be caused by my service, which can be seen here:

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
import {Budget} from "./budget";

@Injectable()
export class BudgetService {

  constructor(public http: Http) { }

  getBudgets() {
    return this.http.get('budget.json')
        .map(response => <Budget[]>response.json().budgetData);

  }
}

If I remove the constructor(public http: Http) { } statement from the service, the test passes fine, but then the app fails in the browser. I have done quite a lot of research on this and have not been able to figure out the solution. Any help would be greatly appreciated!!

Answer

Paul Samsotha picture Paul Samsotha · Oct 12, 2016

The purpose of the TestBed is to configure an @NgModule from scratch for the testing environment. So currently all you have configured is the AppComponent, and nothing else (except the service that's already declared in the @Component.providers.

What I highly suggest you do though, instead of trying to configure everything like you would in a real environment, is to just mock the BudgetService. Trying to configure the Http and mock it is not the best idea, as you want want to keep external dependencies as light as possible when unit testing.

Here's what you need to do

  1. Create a mock for the BudgetService. I would check out this post. You can just extend that abstract class, adding your getBudgets method

  2. You need to override the @Component.providers, as mentioned in this post

If you really want to just use the real service and the Http, then you need to be prepared to mock connections on the MockBackend. You can't use the real backend, as it's dependent on the platform browser. For an example, check out this post. I personally don't think it's a good idea though when testing components. When testing your service, this is when you should do it.