Vitest Browser Modeでカバレッジ計測する
背景
Vitest Browser Modeを使ってTauri+SvelteKitのフロントエンド統合テストを行う
で統合テストはできるようになったのだけど、カバレッジ計測もできると嬉しいなー。
しかし Vitest Browser Mode はまだ実験段階なので、全然情報がない。というか、カバレッジ計測をサポートしていない模様。
そこでそもそもどうやってカバレッジ計測を行うのかを調べて、自前で設定や実装をして実現してみたので、情報共有します。
ここで説明すること
Vitest Browser Mode を使ったテストで、カバレッジ計測を行う設定方法とテストの記述方法を解説します。
私の環境は Tauri 2 + Svelte 5 (SvelteKit) ですが、特に利用するフレームワークには依存しないかと思います。
全体の仕組み
どうやって実現するのか?の概要を説明します。
まず Vitest ではカバレッジ計測の provider に v8 か istanbul が選べます。
v8 は Node.js の V8 エンジンの仕組みを利用する方法なので、ブラウザで動作させる際には利用できません。
カバレッジ計測用コードを埋め込む(instrumentation)タイプの istanbul を使うことになります。
問題は、Vitest の普通の設定方法で provider に istanbul を指定しても、Browser Mode では計測してくれません。
つまり Vitest 自体は Browser Mode ではカバレッジ計測をサポートしていません。
じゃあブラウザ操作に使う provider である Playwright または WebdriverIO のカバレッジ計測の設定を使えばいいかというと、Vitest Browser Mode の設定ファイルでは provider の設定にそれらの設定方法がありません。
ではどうするか?というと・・・以下の流れになります。
- istanbul は
vite-plugin-istanbulを使って plugins に指定しておけば、Vite の仕組みでビルド時に計測コードを instrument してくれる。 - istanbul はカバレッジ計測結果を
window.__coverage__に格納する。 - Vitest Browser Mode には Node.js 側で動作するコマンドをテストから呼び出す仕組みがあり、デフォルトで
writeFileが用意されているので、それを使って計測結果をファイル保存する。 -
nycでそれを HTML 形式の見やすいレポートに変換する。
Vitestの設定
Vitest Browser Mode 用の設定ファイルの plugins に istanbul を適用します。
npm i -D vite-plugin-istanbul などでインストールしておいてください。
import istanbul from 'vite-plugin-istanbul';
// ...
export default defineProject({
plugins: [
tailwindcss(),
sveltekit(),
istanbul({
include: 'src/**',
exclude: [
'**/*.test.ts',
'**/*.browser.test.ts',
'src/integration-tests/**',
],
extension: ['.ts', '.svelte'],
forceBuildInstrument: true,
}),
],
// ...
Vitest の設定ファイルに記述しますが、ここは Vite のビルド時に使うプラグインの設定なので、仕組みとしては Vite を利用しています。
Vite がビルド時に計測用コードを埋め込んで(instruments)してくれるわけです。
テストコードの記述
各テストから利用できるようにカバレッジ出力のコードは別ファイルにしておきます。
src/integration-tests/lib/outputCoverage.ts
import { commands } from 'vitest/browser';
declare global {
interface Window {
__coverage__?: Record<string, unknown>;
}
}
export async function outputCoverage(name: string): Promise<void> {
const coverage = window.__coverage__;
if (coverage) {
await commands.writeFile(`coverage/coverage-${name}.json`, JSON.stringify(coverage, null, 2));
}
}
istanbul はカバレッジ計測結果を window.__coverage__ に格納するので、それを JSON にしてファイルに書き出しています。
ファイルへの書き出しはブラウザでは行えないので、Vitest Browser Mode が用意している Node.js 側で動作するコマンドを利用します。ドキュメントはこちら です。
ファイル操作用の readFile, writeFile, removeFile が用意されていますので、writeFile を利用しています。
(なお自前で Node.js 側で動作するカスタムコマンドを定義することが可能です。それをブラウザ側で動作するテストコードから呼び出せます。しかもそのカスタムコマンドの中で Playwright または WebdriverIO を操作するオブジェクトを利用できます。)
テストファイルには以下を追記します。
import { outputCoverage } from './lib/outputCoverage';
afterAll(async () => {
await outputCoverage('settings-integration');
});
テスト完了時に coverage/coverage-settings-integration.json が出力されます。
改良
テスト名を記述する方式だと、新しいテストを作成する際にコピペして間違って同じ名前で出力するというミスが発生しそうです。
実行中のテストファイルの情報が、import.meta.url で取得可能ですので、それを利用します。
import.meta.url の例: http://127.0.0.1:63315/home/your_name/path_to_project/src/integration-tests/settings.browser.test.ts?import&browserv=1762486222824
カバレッジ出力関数を以下のように変更します。
export async function outputCoverage(url: string): Promise<void> {
const coverage = window.__coverage__;
if (coverage) {
const name = new URL(url).pathname.split('/').pop()?.replace('.browser.test.ts', '');
console.log(`output coverage for test: ${name}`);
await commands.writeFile(`coverage/coverage-${name}.json`, JSON.stringify(coverage, null, 2));
}
}
呼び出す側は以下のようになります。
afterAll(async () => {
await outputCoverage(import.meta.url);
});
カバレッジ計測結果のレポート出力
npm i -D nyc などで nyc というツールをインストールします。
package.json に
// ...
"scripts": {
// ...
"report:coverage": "nyc report --reporter=html --temp-directory=coverage/",
"test:browser": "vitest run --project browser && npm run report:coverage",
"test:all": "vitest run && npm run report:coverage",
// ...
のように登録しておけば、
npm run report:coverage
で coverage/ ディレクトリ以下の計測結果 json ファイルを全て処理して HTML 形式でレポートを coverage/ 以下に出力してくれます。
ついでに
npm run test:browser
など Browser Mode でのテスト時にカバレッジ計測結果レポート作成を行うようにしました。
あとはブラウザで coverage/index.html を開けばレポートが見れます。
まとめ
情報が全然なかったので、Vitest Browser Mode を使ったテストでカバレッジ計測する方法を記事にしました。
仕組みが分かってしまえば結構単純です。
Vitest Browser Mode は確かにカバレッジ計測の仕組みを用意してくれてはいませんが、なくても簡単に実現できるなという印象です。
Discussion