web-dev-qa-db-ja.com

Angular 2 Error:Karma-Jasmine TestにHttpのプロバイダーがありません

アプリがエラーなしで完全に動作しているにもかかわらず、カルマテストで次のエラーが発生し続けます。 Httpのプロバイダーがないと言っています。 app.module.tsファイルで_import { HttpModule } from '@angular/http';_を使用し、それをimports配列に追加しています。カルマエラーは次のようになります。

_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)
_

ここに私の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]);
            });
    }
}
_

これが私の簡単な仕様です。

_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();
  }));
});
_

エラーは私のサービスが原因であるようです。

_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);

  }
}
_

サービスからconstructor(public http: Http) { }ステートメントを削除すると、テストは正常にパスしますが、ブラウザーでアプリが失敗します。私はこれについてかなり多くの研究を行ってきましたが、解決策を見つけることができませんでした。どんな助けも大歓迎です!

19
Chris

TestBedの目的は、テスト環境用に_@NgModule_ 最初からを設定することです。したがって、現在設定しているのはAppComponentnothing elseのみです(_@Component.providers_で既に宣言されているサービスを除く)。

ただし、実際の環境で行うようにすべてを構成するのではなく、BudgetServiceをモックすることを強くお勧めします。 Httpを設定してモックを作成するのは、単体テスト時に外部の依存関係をできるだけ軽くしたいので、最善のアイデアではありません。

ここにあなたがする必要があるものがあります

  1. BudgetServiceのモックを作成します。 この投稿 をチェックアウトします。 getBudgetsメソッドを追加して、その抽象クラスを拡張するだけです

  2. この投稿 で説明されているように、_@Component.providers_をオーバーライドする必要があります

実際のサービスとHttpだけを使用したい場合は、MockBackendで接続をモックする準備をする必要があります。プラットフォームブラウザに依存しているため、実際のバックエンドは使用できません。例については、 this post をご覧ください。個人的には、コンポーネントをテストするときは良い考えだとは思いません。サービスをテストするとき、これがshouldのときです。

24
Paul Samsotha

注意:このソリューションは、静的構造をテストする場合にのみ機能します。テストで実際にサービス呼び出しを行うと機能しません(また、これらのテストの一部を使用した方がよいでしょう)。

テストでは、AppModuleではなく、独自のモジュール定義、テストモジュールを使用します。したがって、HttpModuleもそこにインポートする必要があります。

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

AppModuleをインポートすることもできます:

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

これには、多くの場所で新しいコンポーネントやモジュールを追加する必要がないという利点があります。もっと便利です。一方、これは柔軟性が低くなります。テストで必要以上にインポートしている可能性があります。

さらに、低レベルコンポーネントからAppModule全体への依存関係があります。実際、それは一種の循環依存関係であり、通常は悪い考えです。したがって、私の目では、とにかくアプリケーションの中心となる高レベルのコンポーネントに対してのみそうすべきです。さらに再利用可能な低レベルのコンポーネントについては、テスト仕様にすべての依存関係を明示的にリストする方が適切です。

14
R2C2

オンAngular 4+

RC2Cの答え 私のために働いた:)ありがとう!

注意:これは、notが本当にサービス。静的構造をテストする場合にのみ機能します。

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();
  }));

}

注意:トップを参照注意

4
Filip Savic

HttpModuleをapp.module.tsにインポートすると、問題が解決します。

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

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

peeskillet's answer で説明されているようにサービスをモックする代わりに、 angularが提供するモックバックエンド を使用します。

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();
     }));
});
0
schnatterer