jasmine
TypeScript
angular
webpack

Angularのテストガイドを読みながらテストコードを書いていった際のメモ

More than 1 year has passed since last update.

Testing - ts - GUIDE を読みながらテストコードを書いていった際に調べたこと、躓いたところなどについてまとめていきます(書き途中です)。

TODO: サンプルプロジェクトを作って置いておく

前提

  • たくさん機能があるアプリケーション
  • サーバーサイド: Rails, フロントエンド: Angular
    • (SPAではなく、Rails側からhtml返してから一部をAngular管理化にする感じのアプリケーションです)
  • ソースはTypeScript
  • ビルド周りはWebpack
  • テストランナーにはKarma
  • テストライブラリはJasmine

PipeとServiceには独立した単体テスト、Componentに対してはAngular testing utilities を使おう

You should write isolated unit tests for pipes and services.

You can test components in isolation as well. However, isolated unit tests don't reveal how components interact with Angular. In particular, they can't reveal how a component class interacts with its own template or with other components.

と書いてあります。Angular testing utilities は基本的にComponent用のテストツールだと思ってよさそうです。PipeやServiceに対してテストをする場合はAngularの特別なツールを使わず、JasmineならJasmine, MochaならMochaをそのまま使えば良さそうです。

テストのファイルはソースと同ディレクトリに置こう

angular-cli ベースのプロジェクトではないものの、参考としてangular-cliでてきとうなプロジェクトを生成して、参考にしました。

生成されたプロジェクトをみると、src/app下のソースと同じ場所にテストのファイルも同梱されています。

src/app/
├── app.component.css
├── app.component.html
├── app.component.spec.ts
├── app.component.ts
└── app.module.ts

このディレクトリ構成にするのはAngularの方針らしく、ドキュメントにもその旨が説明されています。

It's a good idea to put unit test spec files in the same folder as the application source code files that they test:

  • Such tests are easy to find.
  • You see at a glance if a part of your application lacks tests.
  • Nearby tests can reveal how a part works in context.
  • When you move the source (inevitable), you remember to move the test.
  • When you rename the source file (inevitable), you remember to rename the test file.

(意訳↓)

  • 見つけやすい
  • テストが足りなければひと目でわかる
  • Nearby tests can reveal how a part works in context.(どういう意味かよくわからなかったので意訳せず)
  • ソースを移動したらテストも移動するのを忘れづらい
  • ソースをリネームしたらテストも移動するのを忘れづらい

個人的にはテストファイルの格納場所はソースと別ディレクトリでも同ディレクトリでもどちらでも良い感じがします。現にサーバーサイド側のRailsではソースとテストは別ディレクトリに置かれてます(その代わり命名規約で関連がわかるようになっている)。

ただ今回はフレームワークが推奨するものに合わせたほうがよさそう、くらいの感覚でAngularのこのガイドに従うことにしました。

Componentのテスト(準備)

Componentのテストには以下の2つを行う必要がありました。

  • テンプレートをコンポーネントのinlineに埋め込む
  • テストダブル(サービスの代わり)をコンポーネントに提供する

テンプレートをコンポーネントのinlineに埋め込む

Angularのテストガイドからは外れますが、angular2-template-loaderというloaderをインストールしてテスト用のwebpack設定ファイルに設定しました。

angular2-template-loader が何をしているかというと、正規表現で templateUrl を捕まえて、template: require('hogehoge.html') の形に変換してます。結果、コンポーネントのメタデータのtemplateプロパティにテンプレートファイルを直に指定したのと同じことになります。

これにより、Angularのテストガイドに書かれている compileComponents() が要らなくなりました。ガイドの方にもきちんと「webpack屋さんには必要ない」みたいなことが書かれています。

WebPack developers need not call compileComponents because it inlines templates and css as part of the automated build process that precedes running the test.

angular2-template-loaderのリポジトリ を見ると、作者が若干やる気無さそうに見えるのがちょっと残念ですが、いざとなったら同じことをするloaderを書くけばいいと思います(正規表現がイマイチでtemplateUrlの書き方次第ではマッチしなかったりするので)。

テストダブル(サービスの代わり)をコンポーネントに提供する

TestBed.configureTestingModule でコンポーネントの設定をするときに、実際のサービスではなく、テストダブルを設定します。

app/my-component.spec.ts
TestBed.configureTestingModule({
  providers: [
      // 本物のServiceではなく, テストダブル(スタブ)をコンポーネントに提供する
      { provide: MyService, useValue: myServiceStub },
  ]
});

これが何をしているのかについてはAngular2のDIを知る - Qiitaを読んで理解することができました。要はmyServiceStubをMyServiceとしてコンポーネントに提供するという意味です。

Componentのテスト(本体)

Componentをテストする流れはこんな感じになりそうです。

  • TestBed.configureModule() でコンポーネントの設定をする
  • TestBed.createComponent() でコンポーネントを生成する
  • コンポーネントをお好みの状態にする
    • (例: 「ブログ記事が0件の状態のテストをしたい」 => componentInstance.posts = [];
  • detectChanges() で変更検知をする
  • debugElement から要素をとってきて表示のテストをしたりする

ひとまずこれで、Angularのコンポーネントをテストできるようになりました :tada:

ダミーデータを使う

APIの実レスポンスを利用するわけにもいかないので、テスト実行時にAPIの返り値の代わりとなるダミーデータが必要です。コンポーネントのテストではServiceのメソッドを叩くこと・叩いたあとにプロパティが期待した状態になっていること、のテストで十分かもしれませんが、APIを叩くService自身のテストをするにはダミーデータが必要になります。

そこで、test/fixtures/user.json のようなファイルを作り、それを読み込ませるようにしました。

以下のリンクを参考にして、karma.conf.jsのfilesにダミーデータのパスを加えて、included: false を指定する変更を加えました。

参考