lacolaco's marginalia

Reading and Developing

Angular: Vitest Browser Modeへの移行とTesting Library Custom Matcher

結論

VitestのBrowser Modeで使うexpect APIにははじめからDOM要素向けカスタムマッチャが組み込まれているので @testing-library/jest-dom は必要ない。

Vitest移行とTesting Library

Angularプロジェクトのユニットテスト環境をAngular CLI公式のVItestサポートに移行しても、Angular Testing Libraryは変わらず使える。いまとなってはTesting LibraryなしでAngularコンポーネントのテストを書くことは考えたくない。

Testing Libraryを使ってテストを書く場合、アサーション用のカスタムマッチャを導入しているケースのほうが多いだろう。toBeVisibletoBeDisabledのようにDOM特有のアサーションを簡単にしてくれる。

expect(screen.queryByTestId('not-empty')).not.toBeEmptyDOMElement()
expect(screen.getByText('Visible Example')).toBeVisible()

Karma/Jasmineであれば@testing-library/jasmine-dom 、Jestであれば @testing-library/jest-dom をインストールして、テストセットアップファイルでカスタムマッチャの登録をする必要がある。VitestのNode.js実行モードで同じことをするには、@testing-library/jest-dom/vitestをインポートすればよい。Jest版と同じカスタムマッチャをVitest互換で提供してくれる。

Vitest Browser ModeのAssertion

しかし、AngularのユニットテストをVitestに移行したうえで、実行環境をNode.jsではなくBrowser Modeにした場合は事情が変わる。

Browser Modeで使えるexpectには、はじめから@testing-library/jest-dom と同等の組み込みマッチャが存在する。なので何も導入する必要がない。

Vitest provides a wide range of DOM assertions out of the box forked from @testing-library/jest-dom library with the added support for locators and built-in retry-ability.

さらにカスタムマッチャだけでなく、expect.elementというAPIによって、タイミングによってflakyになりがちなDOM要素の取得を自動リトライしてくれる機能もある。これまではTesting LibraryのwaitForなどで工夫する必要があったが、これからはexpect.elementを使ってもいいだろう。

import { expect, test } from 'vitest'
import { page } from 'vitest/browser'

test('error banner is rendered', async () => {
  triggerError()

  // This creates a locator that will try to find the element
  // when any of its methods are called.
  // This call by itself doesn't check the existence of the element.
  const banner = page.getByRole('alert', {
    name: /error/i,
  })

  // Vitest provides `expect.element` with built-in retry-ability
  // It will repeatedly check that the element exists in the DOM and that
  // the content of `element.textContent` is equal to "Error!"
  // until all the conditions are met
  await expect.element(banner).toHaveTextContent('Error!')
})

Angular CLIのVitest Browser Modeサポート

Angular v21.0アップデート時点ではBrowser Modeへのマイグレーションは用意されていなかったが、v21.2に向けてサポートが入ってきている。

このコミットはng add @vitest/browser-playwright のように、ng addコマンドでVitestのBrowser Mode用モジュールを導入できるようにするものだ。ただパッケージをインストールするだけでなく、設定ファイルの自動セットアップをしてくれる。あらかじめVitest実行環境に移行できている必要はあるが、ng newで作成した直後のプロジェクトからあっという間にBrowser Modeに移行できるだろう。

Karma/Jasmine環境からVitestへの移行は既存テストのJasmine依存が強いほど大変ではあるが、しかしひと手間だけ我慢して一気にBrowser Modeまで導入してしまうことを個人的にはおすすめしたい。実行環境を実ブラウザのまま維持したほうが、JSDOMとの間の互換性の心配をしなくていいからだ。逆にもともとJest環境だった場合はBrowser Modeには移行せずデフォルトのNode.jsモードのままでいいだろう。

まとめ

Vitest Browser Modeへの移行により、@testing-library/jest-domのような追加パッケージが不要になり、DOM要素向けカスタムマッチャが標準で使える。またexpect.elementによる自動リトライ機能も組み込まれている。Angular v21.2以降ではng addコマンドによる導入が可能になる予定だ。

Karmaが非推奨だからという消極的な理由だけでなく、モダンな開発者体験の恩恵を受けるためにも、なるべく多くのAngularプロジェクトでVitestに移行してもらいたい。