简体   繁体   English

Angular 2错误:在Karma-Jasmine测试中没有Http的提供者

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

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. 据说没有Http的提供者。 I'm using import { HttpModule } from '@angular/http'; 我正在使用import { HttpModule } from '@angular/http'; in my app.module.ts file and adding it to the imports array. 在我的app.module.ts文件中,并将其添加到imports数组中。 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: 这是我的app.component.ts文件:

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. 如果我从服务中删除constructor(public http: Http) { }语句,测试传递正常,但随后应用程序在浏览器中失败。 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!! 任何帮助将不胜感激!!

The purpose of the TestBed is to configure an @NgModule from scratch for the testing environment. TestBed的目的是从头开始为测试环境配置@NgModule So currently all you have configured is the AppComponent , and nothing else (except the service that's already declared in the @Component.providers . 因此,目前您配置的所有内容都是AppComponent没有其他内容(除了已经在@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 . 我强烈建议你这样做,而不是试图像在真实环境中那样配置所有内容,只是模拟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. 尝试配置Http并模拟它并不是最好的主意,因为您希望在单元测试时尽可能保持外部依赖性尽可能轻。

Here's what you need to do 这是你需要做的

  1. Create a mock for the BudgetService . BudgetService创建一个模拟。 I would check out this post . 我会查看这篇文章 You can just extend that abstract class, adding your getBudgets method 你可以扩展那个抽象类,添加你的getBudgets方法

  2. You need to override the @Component.providers , as mentioned in this post 你需要重写@Component.providers ,在提到这个岗位

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 . 如果你真的想要使用真实服务和Http ,那么你需要准备在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. 在测试您的服务时,您应该这样做。

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. 您的测试使用自己的模块定义,测试模块,而不是您的AppModule。 So you have to import HttpModule there, too: 所以你必须在那里导入HttpModule:

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

You can also import your AppModule: 您还可以导入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. 此外,您具有从低级组件到整个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. 对于可能可重用的更多低级组件,最好在测试规范中明确列出所有依赖项。

On Angular 4+ 关于Angular 4+

RC2C's answer worked for me :) Thanks! RC2C的答案对我有用 :)谢谢!

Caution : This will only work if you're not really calling your service. 警告 :只有在您没有真正打电话给您的服务时才会有效。 It works only if you want to test the static structure. 仅当您要测试静态结构时,它才有效。

Just wanted to add that for Angular version 4 (and higher, probably) you should import HttpClientModule to your test bed, so that it looks like this: 只想添加Angular版本4(可能更高版本),你应该将HttpClientModule导入你的测试床,这样它看起来像这样:

import { HttpClientModule } from '@angular/common/http';


describe('BuildingService', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientModule],
      providers: [BuildingService]
    });
  });

  it('should be created 2', inject([BuildingService], (service: BuildingService) => {
    expect(service).toBeTruthy();
  }));

}

Caution : See top Caution 小心 :请参阅顶部注意

Import HttpModule in app.module.ts and it will solve your problem. 在app.module.ts中导入HttpModule,它将解决您的问题。

import { HttpModule } from '@angular/http';

@NgModule({
    imports: [HttpModule]
})
...

An alternative to mocking the service as described in peeskillet's answer , is using the Mock Backend provided by angular . peeskillet的回答所述, 模拟服务的另一种方法是使用由角度提供模拟后端

The API doc contains the following example: API文档包含以下示例:

import {Injectable, ReflectiveInjector} from '@angular/core';
import {async, fakeAsync, tick} from '@angular/core/testing';
import {BaseRequestOptions, ConnectionBackend, Http, RequestOptions} from '@angular/http';
import {Response, ResponseOptions} from '@angular/http';
import {MockBackend, MockConnection} from '@angular/http/testing';

const HERO_ONE = 'HeroNrOne';
const HERO_TWO = 'WillBeAlwaysTheSecond';

@Injectable()
class HeroService {
  constructor(private http: Http) {}

  getHeroes(): Promise<String[]> {
    return this.http.get('myservices.de/api/heroes')
        .toPromise()
        .then(response => response.json().data)
        .catch(e => this.handleError(e));
  }

  private handleError(error: any): Promise<any> {
    console.error('An error occurred', error);
    return Promise.reject(error.message || error);
  }
}

describe('MockBackend HeroService Example', () => {
  beforeEach(() => {
    this.injector = ReflectiveInjector.resolveAndCreate([
      {provide: ConnectionBackend, useClass: MockBackend},
      {provide: RequestOptions, useClass: BaseRequestOptions},
      Http,
      HeroService,
    ]);
    this.heroService = this.injector.get(HeroService);
    this.backend = this.injector.get(ConnectionBackend) as MockBackend;
    this.backend.connections.subscribe((connection: any) => this.lastConnection = connection);
  });

  it('getHeroes() should query current service url', () => {
    this.heroService.getHeroes();
    expect(this.lastConnection).toBeDefined('no http service connection at all?');
    expect(this.lastConnection.request.url).toMatch(/api\/heroes$/, 'url invalid');
  });

  it('getHeroes() should return some heroes', fakeAsync(() => {
       let result: String[];
       this.heroService.getHeroes().then((heroes: String[]) => result = heroes);
       this.lastConnection.mockRespond(new Response(new ResponseOptions({
         body: JSON.stringify({data: [HERO_ONE, HERO_TWO]}),
       })));
       tick();
       expect(result.length).toEqual(2, 'should contain given amount of heroes');
       expect(result[0]).toEqual(HERO_ONE, ' HERO_ONE should be the first hero');
       expect(result[1]).toEqual(HERO_TWO, ' HERO_TWO should be the second hero');
     }));

  it('getHeroes() while server is down', fakeAsync(() => {
       let result: String[];
       let catchedError: any;
       this.heroService.getHeroes()
           .then((heroes: String[]) => result = heroes)
           .catch((error: any) => catchedError = error);
       this.lastConnection.mockRespond(new Response(new ResponseOptions({
         status: 404,
         statusText: 'URL not Found',
       })));
       tick();
       expect(result).toBeUndefined();
       expect(catchedError).toBeDefined();
     }));
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM