[Angular] AoT Compile結果を単体テスト時に利用する


@Quramy です。

さて、先日 ng-japan というイベントに登壇し、Angularとテストについて喋らせてもらいました。

資料はこちら

実は時間があまったら追加で喋ろうかな、と思っていたコンテンツがあったのですが、結果的には完全に時間オーバーしていたため、Qiitaの方に書いておきます。

Use AOT in JIT and AOT unit testing

「AoTなのか、JiTなのかどっちだよ!」という突っ込みが聞こえてきそうですね1

Angular v4.2系から TestBed#initTestEnvironmentにオプショナルな引数として aotSummaries というのが追加されました。
AoT Compile済みのNgFactoryを単体テストからも利用できるようになるという素敵野郎です。

今日(2017.06.19)時点では、殆どドキュメントも無いので、対応するcommitのリンク貼っておきますが、下記あたりです。

どのようなメリットがあるのか

この機能を活用すると、以下のメリットがあります。

  • ComponentのCompileをすっ飛ばせる
  • より本番に近しいコードでテストを実行可能

特に嬉しいのが1番目の「ComponentのCompileをすっ飛ばせる」です。
AngularのComponent 単体テストでComponentFixtureを用いてテスト対象のComponentをマウントする場合、各spec毎でテスト対象ComponentのCompileが実行されます。
特にIntegration Testing Pattern2 を利用している場合、テスト対象のComponentでは必要のないComponentであっても、依存Moduleとしてimportsに登録していると、当該Moduleのdeclarationsに指定された全てのComponentに対してCompileが実行されます。

ComponentのCompileがテストの実行時間にどの程度効いてくるかについては、以前 Performance Angular unit testing - Mediumで測定や改善について記載しているため、詳細は省きますが、酷いと「Karma実行時間中の70%以上がCompileに要する時間だった」ということもザラです。

下図は実際にプロジェクトで使っているAngularを4.2系にupgradeした際に計測した比較です(左: AoTなし / 右: AoTあり)

Karmaの実行時間が9分台から2分台まで削減されました3。爆速!

どうやって適用するの?

@angular/cliにおけるAoTビルド(ng build --aot)のように、ng test コマンドにオプションが用意されていればよいのですが、この機能はまだ実装されていません4
まぁそのうち使えるようになるとは思うのですが、どうしてもすぐに使いたい!という人向けに雑なスクリプトを組んでみました。

test-aot.sh
#!/bin/sh

# 1. Create ngsummary.ts
cat << EOF > tsconfig.aottest.json
{
  "extends": "./tsconfig.json",
  "angularCompilerOptions": {
    "genDir": ".",
    "debug": false,
    "enableSummariesForJit": true
  }
}
EOF
./node_modules/.bin/ngc -p ./tsconfig.aottest.json

# 2. Create an entry file for AOT testing
echo "import { AppModuleNgSummary } from './app/app.module.ngsummary';" > src/test_aot.ts
sed -E "s/platformBrowserDynamicTesting\(\)/platformBrowserDynamicTesting(), () => [AppModuleNgSummary],/g" src/test.ts >> src/test_aot.ts
mv src/test.ts src/test.ts.bk && mv src/test_aot.ts src/test.ts

# 3. Run karma
ng test --single-run
CODE=$?

# 4. Cleanup temp files
mv src/test.ts.bk src/test.ts
rm tsconfig.aottest.json
node node_modules/rimraf/bin.js src/**/*.ngfactory.* src/**/*.ngsummary.* src/**/*.shim.ngstyle.ts
node node_modules/rimraf/bin.js e2e/**/*.ngfactory.* e2e/**/*.ngsummary.* e2e/**/*.shim.ngstyle.ts

exit $CODE

※ レポジトリは https://github.com/Quramy/test-with-aot-summary にあります。

ポイントは下記あたりでしょうか:

  • AoT Compile時に enableSummariesForJit を有効にする必要がある
  • test.tsでCompile結果を読み込む必要がある
test.ts
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting(),
  () => [AppModuleNgSummary] // こいつが重要
);

テスト時間が爆増して困っている!という方はお試しください。

参考・脚注


  1. 節タイトルはDesign Docからパチっています 

  2. Three Ways to Test Angular Components 

  3. 実のところ、4.1.xまででも散々テスト実行時間には苦労していて、自分でCompile時間を削減する仕組みを作ったりしていました。 https://speakerdeck.com/quramy/haunted-house-of-angular-unit-testing 

  4. https://github.com/angular/angular-cli/issues/6650 triageされているので、そのうち使えるようにはなるはず。