Angular2 Unit Testing - XHRのテスト

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

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

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

hero.service.spec.ts

1
2
3
4
5
6
7
8
9
10
11
12
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 https://github.com/mitsuruog/angular2-minimum-starter