Cover image for Angular2 Unit Testing - XHRのテスト
angularangular2karmajasmineunit test

Angular2 Unit Testing - XHRのテスト

March 20, 2016

7 min read

mitsuruogMitsuru Ogawa

Angular2 の実装の方法は記事をよく目にする機会が増えたので、テストについての自分が困らないように調べてみたシリーズ。

今回は XHR が関連するテスト。

(注意)Angular 2.0.0-beta.9 をベースに話しています。 E2E テストは protractor がそのまま利用できると思うので、ここでのテストはユニットテストの話です。

Angular2 Unit Testing

  1. 準備
  2. 基本
  3. Mock, Spy の基本(TBD)
  4. DOM のテスト
  5. XHR のテスト
  6. Component のテスト(TBD)
  7. Service のテスト(TBD)
  8. Pipe のテスト
  9. Directive のテスト(TBD)
  10. カバレッジ

XHR のテスト

XHR 通信が関連するテストについて紹介します。

Angular1 には XHR 通信を行う$httpサービスがありました。 XHR 通信をテストする場合は、$httpを Mock する$httpBackendを利用し、実際の XHR 通信を$httpBackendでダミーのレスポンスに置き換えてテストを行っていました。

Angular2 にもMockBackendという$httpBackendと同様の役割のものがありますので、利用方法について紹介します。

標準 HTTP の Mock を準備する(beforeEachProviders)

MockBackend@Injectable指定されているため、他の Provider と同様にbeforeEachProvidersで呼び出した後に、injectでテストコード中に DI して利用する必要があります。

hero.service.spec.ts

describe("HTTPのテスト", () => {
  beforeEachProviders(() => [
    BaseRequestOptions,
    MockBackend,
    provide(Http, {
      useFactory: function (backend, defaultOptions) {
        return new Http(backend, defaultOptions);
      },
      deps: [MockBackend, BaseRequestOptions],
    }),
    HeroService,
  ]);

  // ...省略
});

上のコードでは、ProviderAPI を利用して、テスト専用のHTTPを生成しています。 Providerは、Angular2 の中で@Injecter()によって DI がされる際にインスタンスの生成方法を指定する API です。

ここでは、dependenciesHTTPが利用している Provider を DI し、useFactory内部で新しいHTTPインスタンスを作成しています。 インスタンスを作成する際のbackendに、dependenciesで指定したMockBackendが設定されます。

dependenciesに設定している各 Provider は、後にダミーのレスポンスを返却する設定を DI するための楔(くさび)だと考えてください。

ダミーのレスポンスを設定する(inject)

続いてダミーのレスポンスを設定します。

先に生成したHTTPがテスト対象の Service に DI されるタイミングで、injectを利用してMockBackendのレスポンスをダミーで書き換えます。

hero.service.spec.ts

describe("HTTPのテスト", () => {
  // ...省略

  beforeEach(inject([MockBackend], (backend: MockBackend) => {
    const response = new Response(
      new ResponseOptions({
        body: {
          data: [
            { id: "1", name: "mitsuru" },
            { id: "2", name: "ogawa" },
          ],
        },
      })
    );
    backend.connections.subscribe((conn: MockConnection) =>
      conn.mockRespond(response)
    );
  }));

  // ...省略
});

ダミーのレスポンスを返却する部分は少しややこしいですが、まずは形から入りましょう。

大事な部分はResponseOptionsにダミーのレスポンスを設定する部分です。 上の例では、body のdataプロパティにダミーのオブジェクトを設定しています。他にも設定できるプロパティが存在するので、詳しくは API を参照してください。

実際のレスポンスはMockConnectionを利用して行います。他にもmockDownload, mockErrorなど使えそうな API があります。

ResponseOptionsの部分はダミーレスポンス専用のクラスにしておき、テストコードの先頭で import して利用するともっとスッキリすると思います。

テストを検証する(it)

では XHR 通信が正しく Mock されているか検証します。

その前に、テスト対象の HeroService についてです。 HeroService は、Heroのリストを XHR 通信で取得した後に、Observable でラップして返却する(Angular2 的には)シンプルなデータアクセスクラスだとします。

hero.service.ts

@Injectable()
export class HeroService {
  getHeroes(): Observable<Hero[]> {
      return this.http.get(this.URL)
        .map(res => <Hero[]>res.json().data);
    }
}

先ほどの設定してダミーレスポンスを実際に検証してみます。

hero.service.spec.ts

describe("HTTPのテスト", () => {
  // ...省略

  it("2件取得できること", inject([HeroService], (testee: HeroService) => {
    testee.getHeroes().subscribe((res: Hero[]) => {
      expect(res.length).toEqual(2);
      expect(res[0].name).toEqual("mitsuru");
    });
  }));
});

無事、2 件取得できることが確認できました。 めでたし。めでたし。

まとめ

HTTP のテストについてでした。

テストの全体像についてはこちらを参考にしてください。

https://github.com/mitsuruog/_angular2_http

Unit テスト全般に言えることですが、テストを行うための Mock などの下準備が非常に面倒くさいです。 下準備は定型文的なものが多いため、再利用できるようスニペットなどにしておくことが大事です。

今回は、Service クラスを題材にしたため、テストコードの中で HTTP を置き換えています。 Component など HTTP から遠いレイアーでは、今回のように HTTP を置き換えるのではなく、Service クラスごと Mock 化してbeforeEachProvidersで Service クラスを置き換え方がテストしやすいと思います。

PR

こちらに初学者のための Minimum starter kit を作成しましたので、ぜひ利用してください。 (もちろんテストもできます!!)

mitsuruog/angular2-minimum-starter: Minimum starter kit for angular2