Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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!!

like image 700
Chris Avatar asked Oct 12 '16 16:10

Chris


2 Answers

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.

like image 88
Paul Samsotha Avatar answered Oct 24 '22 08:10

Paul Samsotha


Caution: This solution only works if you want to test the static structure. It won't work if your test actually makes service calls (and you better also have some of those tests).

Your test uses an own module definition, a testing module, and not your AppModule. So you have to import HttpModule there, too:

TestBed.configureTestingModule({     imports: [         HttpModule     ],     declarations: [         AppComponent     ] }); 

You can also import your AppModule:

TestBed.configureTestingModule({     imports: [         AppModule     ] }); 

This has the advantage that you don't have to add new components and modules at many places. It's more convenient. On the other hand this is less flexible. You may be importing more than you'd wish in your test.

Furthermore you have a dependency from your low-level component to the whole AppModule. In fact that's kind of a circular dependency which is normally a bad idea. So in my eyes you should only do so for high-level components that are very central to your application anyway. For more low-level components which may be even reusable, you better list all dependencies explicitly in the test spec.

like image 30
R2C2 Avatar answered Oct 24 '22 07:10

R2C2