lacolaco's marginalia

Angular CLIの新しいUnit Testing Runnerを試す

Angular v20ではAngular CLIに新しいユニットテスト実行機能が実装されている。esbuildベースの新しい安定版機能と、それとは別の実験的機能も提供されはじめた。現在の状況を確認しておこう。

Karma Runner (@angular/build:karma)

現在の安定したユニットテストランナー機能としてデフォルトで提供されているのは、 @angular/build:karma ビルダーを使ったesbuildベースのKarmaランナーだ。従来のデフォルトだったWebpackベースの @angular-devkit/build-angular:karma と基本的に互換性が保たれていて、builderを書き換えるだけでesbuildベースへ切り替わり、ビルドが安定かつ高速になる。

  "test": {
    "builder": "@angular/build:karma",
    "options": {
      "polyfills": ["zone.js", "zone.js/testing"],
      "tsConfig": "tsconfig.spec.json",
      "assets": ["src/favicon.ico", "src/assets"],
      "styles": ["src/styles.css"],
      "browsers": "ChromeHeadless"
    }
  }

テストランナーの振る舞いは @angular-devkit/builder-angular:karma ビルダーで builderMode: “application” に設定したときと変わらないが、@angular-devkit/builder-angular パッケージへの依存を捨てることでnode_modulesの中からwebpack関連の推移的依存関係がなくなることが利点だ。

ここを現在地として、Karmaからモダンなテスト実行ツールセットへ移行していくのが今のAngular CLIのロードマップであり、そのための新しいビルダーが @angular/build:unit-test だ。

Karma Runner (@angular/build:unit-test)

Angular v20から提供が始まった実験的機能が @angular/build:unit-test ビルダーだ。このビルダーはこれまでのユニットテスト用ビルダーとは別に作り直されたもので、特定のテストフレームワークやテストランナーに依存しないAPIになっているのが特徴だ。

現在unit-testビルダーが対応しているのはKarmaとVitestの2つだが、今後もおそらく増えるだろう。まずはKarmaを動かすのにどのように設定するのかを確認しよう。

次の設定は上述のものと意味的には同じになる新しい書き方だ。オプションのrunnerフィールドではどのツールを使うかを表し、今回はkarmaを指定する。buildTargetはテストのためのビルド設定をng build の設定から参照するための文字列で、::development<projectName>:build:development を意味する短縮表現だ。つまり複数プロジェクトであれば別のプロジェクトを指定することもできる。ほかはお馴染みの設定項目だ。

  "test": {
    "builder": "@angular/build:unit-test",
    "options": {
      "runner": "karma",
      "buildTarget": "::development",
      "tsConfig": "tsconfig.spec.json",
      "browsers": ["ChromeHeadless"]
    }
  }

これだけの指定で、基本的にはこれまでどおりKarma/Jasmineで書かれたテストなら実行できる。karma.conf.jstest.ts でのカスタマイズが多い場合はまだ対応しきれないかもしれないが、実験的機能なので大目に見てほしい。おそらく今後対応されると思われる。

ちなみに、記事を書いている2025-06-25時点(v20.0.3)では、テスト実行時に zone.js/testing を自動的に読み込む処理が動いておらず、fakeAsyncに依存したテストが失敗する。この問題のイシューとPRを提出してレビューが通ったので、近いうちに解決するはず。

Vitest Runner

さて、このunit-testビルダーの注目ポイントはメンテナンスモードに入った非推奨状態のKarmaではなく、モダンなテストランナー Vitest を選択できることだ。

Vitestを使うために以下のように runnerオプションにvitestを指定する。それ以外の設定項目はKarmaを使うときとほとんど変わらない。

  "test": {
    "builder": "@angular/build:unit-test",
    "options": {
      "runner": "vitest",
      "tsConfig": "tsconfig.spec.json",
      "buildTarget": "::development"
    }
  },

この状態でng testコマンドを実行すると、不足しているパッケージのインストールを促される。

> ng test

NOTE: The "unit-test" builder is currently EXPERIMENTAL and not ready for production use.
The `vitest` package was not found. Please install the package and rerun the test command.

まずはvitestパッケージをインストールしよう。

pnpm i -D vitest

インストール後にもう一度実行すると、次はDOMテストのためにJSDOMをインストールするように促される。デフォルトではVitestはNode.js上でDOMエミュレーションによるテストを行う。

 MISSING DEPENDENCY  Cannot find dependency 'jsdom'

 Do you want to install jsdom? yes

unit-testビルダーにbrowsersオプションを与えると、Node.jsでのエミュレーションではなく実際のブラウザでテストを実行する。これはVitestのBrowser Modeを利用するためJSDOMは不要だ。

  "test": {
    "builder": "@angular/build:unit-test",
    "options": {
      "runner": "vitest",
      "tsConfig": "tsconfig.spec.json",
      "buildTarget": "::development",
      "browsers": ["chromium"]
    }
  }

設定を変更してテストを実行すると、追加のパッケージインストールを求められる。VitestのプラグインとPlaywrightのnpmパッケージをインストールし、PlaywrightのセットアップをすればOKだ。

pnpm i -D @vitest/browser playwright
npx playwright install
ng testの実行中画面(Vitest)
ng testの実行中画面(Vitest)

デフォルトではVitestのWeb UIが立ち上がりwatchモードに入る。 --no-watch フラグを付けるか、ビルダーの設定にwatch: falseを加えれば一回限りの実行で結果を返すようになる。

ng test --no-watch

実際に試したサンプルプロジェクトはGitHubで公開している。つまづいたら手元で動かしてみてほしい。

まとめ

Angular v20では、これまでの@angular-devkit/build-angular:karma に置き換わる新しいesbuildベースの@angular/build:karmaビルダーがデフォルトになった。まだ従来のビルダーに依存しているプロジェクトは、まずこの移行を済ませることが重要だ。

また、実験的機能 @angular/build:unit-test ビルダーにより、KarmaからVitestへの移行が段階的に進められるようになりつつある。第一段階ではKarma/Jasmine構成のままでビルダーだけを切り替える。第二段階では設定をほとんど変えないままにテストランナーを切り替えることができる。

ユニットテスト実行環境がプロジェクト作成直後からデフォルトで整うのがAngular CLIの大きな利点のひとつだ。今後のアップデートの恩恵を受けるため、サードパーティに依存してJestやVitestを利用しているプロジェクトでも、様子を見ながら本家に帰ってくる準備を始めてもいいだろう。