はじめに
この記事では、Vitest でコードカバレッジを取得する方法を解説します。
コードカバレッジは、テストがソースコードのどの程度をカバーしているかを測定する指標です。Vitest は v8 と istanbul の2つのカバレッジプロバイダーをサポートしており、簡単にカバレッジレポートを生成できます。
開発環境
開発環境は以下の通りです。
- Windows 11
- VSCode
- Vite 7.3.1
- React 19.2.3
- TypeScript 5.9.3
- Vitest 4.0.17
- @vitest/coverage-v8 4.0.17
カバレッジプロバイダーの選択
Vitest では2つのカバレッジプロバイダーから選択できます。
| プロバイダー | 特徴 |
|---|---|
| v8(デフォルト) | Chrome V8 エンジンのネイティブカバレッジ機能を使用。高速で、Vitest 3.2.0 以降は AST ベースの解析により Istanbul と同等の精度 |
| istanbul | ソースコードを事前にインストルメント(計測コードを挿入)。長年の実績があり、JavaScript ランタイムを問わず動作 |
Vitest 3.2.0 以降では v8 プロバイダーでも Istanbul と同等の精度が得られるようになったため、特別な理由がない限り v8(デフォルト)の使用を推奨します。
インストール
カバレッジ機能を使用するには、対応するパッケージをインストールします。
v8 プロバイダーの場合(推奨)
npm install -D @vitest/coverage-v8
istanbul プロバイダーの場合
npm install -D @vitest/coverage-istanbul
初めてカバレッジを実行する際、パッケージがインストールされていなければ Vitest が自動的にインストールを促します。
基本的な設定
vite.config.ts にカバレッジの設定を追加します。
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
coverage: {
provider: 'v8', // 'v8' または 'istanbul'
reporter: ['text', 'json', 'html'], // レポート形式
include: ['src/**/*.{ts,tsx}'], // カバレッジ対象
exclude: [
'src/**/*.test.{ts,tsx}',
'src/**/*.spec.{ts,tsx}',
'src/test/**',
'src/main.tsx',
],
},
},
})
カバレッジの実行
CLI でカバレッジを有効化
npx vitest --coverage
ウォッチモードでカバレッジを有効化
npx vitest --coverage
単発実行でカバレッジを取得
npx vitest run --coverage
成功すると、以下のようなレポートが表示されます。
✓ src/components/__tests__/Counter.spec.tsx (4 tests) 234ms
Test Files 1 passed (1)
Tests 4 passed (4)
Start at 10:00:00
Duration 1.23s
% Coverage report from v8
-----------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------------------|---------|----------|---------|---------|-------------------
All files | 85.71 | 100 | 80 | 85.71 |
Counter.tsx | 85.71 | 100 | 80 | 85.71 | 12-13
-----------------------|---------|----------|---------|---------|-------------------
カバレッジレポートの種類
主なレポーター
| レポーター | 説明 |
|---|---|
text |
ターミナルにテーブル形式で表示(デフォルト) |
html |
HTML 形式のレポートを生成(coverage/index.html) |
json |
JSON 形式のレポートを生成 |
lcov |
LCOV 形式(CI ツールとの連携に使用) |
clover |
Clover XML 形式 |
cobertura |
Cobertura XML 形式 |
複数のレポーターを使用
export default defineConfig({
test: {
coverage: {
reporter: ['text', 'html', 'lcov', 'json'],
},
},
})
テスト対象のサンプルコード
カバレッジを計測するサンプルコードを作成します。
export const add = (a: number, b: number): number => {
return a + b
}
export const subtract = (a: number, b: number): number => {
return a - b
}
export const multiply = (a: number, b: number): number => {
return a * b
}
export const divide = (a: number, b: number): number => {
if (b === 0) {
throw new Error('Cannot divide by zero')
}
return a / b
}
export const calculate = (
operation: 'add' | 'subtract' | 'multiply' | 'divide',
a: number,
b: number
): number => {
switch (operation) {
case 'add':
return add(a, b)
case 'subtract':
return subtract(a, b)
case 'multiply':
return multiply(a, b)
case 'divide':
return divide(a, b)
default:
throw new Error(`Unknown operation: ${operation}`)
}
}
テストコード
import { describe, expect, test } from 'vitest'
import { add, subtract, multiply, divide, calculate } from '../calculator'
describe('Calculator', () => {
describe('add', () => {
test('2つの数値を足し算できる', () => {
expect(add(1, 2)).toBe(3)
expect(add(-1, 1)).toBe(0)
expect(add(0, 0)).toBe(0)
})
})
describe('subtract', () => {
test('2つの数値を引き算できる', () => {
expect(subtract(5, 3)).toBe(2)
expect(subtract(1, 1)).toBe(0)
expect(subtract(0, 5)).toBe(-5)
})
})
describe('multiply', () => {
test('2つの数値を掛け算できる', () => {
expect(multiply(2, 3)).toBe(6)
expect(multiply(-2, 3)).toBe(-6)
expect(multiply(0, 100)).toBe(0)
})
})
describe('divide', () => {
test('2つの数値を割り算できる', () => {
expect(divide(6, 2)).toBe(3)
expect(divide(10, 4)).toBe(2.5)
})
test('0で割るとエラーが発生する', () => {
expect(() => divide(10, 0)).toThrow('Cannot divide by zero')
})
})
describe('calculate', () => {
test('add 操作で足し算ができる', () => {
expect(calculate('add', 1, 2)).toBe(3)
})
test('subtract 操作で引き算ができる', () => {
expect(calculate('subtract', 5, 3)).toBe(2)
})
test('multiply 操作で掛け算ができる', () => {
expect(calculate('multiply', 2, 3)).toBe(6)
})
test('divide 操作で割り算ができる', () => {
expect(calculate('divide', 6, 2)).toBe(3)
})
})
})
カバレッジしきい値の設定
チームで最低限のカバレッジを維持するために、しきい値を設定できます。しきい値を下回るとテストが失敗します。
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
// グローバルしきい値
thresholds: {
statements: 80,
branches: 80,
functions: 80,
lines: 80,
},
},
},
})
ファイルごとのしきい値
export default defineConfig({
test: {
coverage: {
thresholds: {
// グローバルしきい値
statements: 80,
branches: 80,
functions: 80,
lines: 80,
// ファイルごとのしきい値(グローバルを上書き)
'src/utils/calculator.ts': {
statements: 100,
branches: 100,
functions: 100,
lines: 100,
},
},
},
},
})
カバレッジから除外する
設定ファイルで除外
export default defineConfig({
test: {
coverage: {
include: ['src/**/*.{ts,tsx}'],
exclude: [
'src/**/*.test.{ts,tsx}',
'src/**/*.spec.{ts,tsx}',
'src/**/*.d.ts',
'src/test/**',
'src/mocks/**',
'src/main.tsx',
'src/vite-env.d.ts',
],
},
},
})
コメントで特定のコードを除外
v8 プロバイダーの場合、/* v8 ignore */ コメントを使用します。
/* v8 ignore next -- @preserve */
function ignoredFunction() {
// この関数全体がカバレッジから除外される
console.log('This is ignored')
}
function someFunction(condition: boolean) {
/* v8 ignore if -- @preserve */
if (condition) {
// この if ブロックがカバレッジから除外される
console.log('Ignored')
} else {
console.log('Included')
}
}
/* v8 ignore next -- @preserve */
const ignoredLine = 'This line is ignored'
istanbul プロバイダーの場合は /* istanbul ignore */ を使用します。
/* istanbul ignore next -- @preserve */
function ignoredFunction() {
console.log('This is ignored')
}
/* istanbul ignore if -- @preserve */
if (process.env.NODE_ENV === 'development') {
console.log('Development only')
}
TypeScript を使用する場合、esbuild によるトランスパイル時にコメントが削除されることがあります。-- @preserve を追加することでコメントを保持できます。
Vitest UI でカバレッジを確認
Vitest UI を使用すると、ブラウザ上でカバレッジレポートを視覚的に確認できます。
npm install -D @vitest/ui
UI モードでカバレッジを確認するには、以下の設定が必要です。
export default defineConfig({
test: {
coverage: {
enabled: true, // カバレッジを有効化
reporter: ['text', 'html'], // html レポーターが必要
},
},
})
実行コマンド:
npx vitest --ui --coverage
ブラウザが開き、Coverage タブでカバレッジレポートを確認できます。
CI/CD での活用
GitHub Actions での設定例
# .github/workflows/test.yml
name: Test
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests with coverage
run: npm run test:coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
files: ./coverage/lcov.info
fail_ci_if_error: true
package.json のスクリプト例
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage",
"test:ui": "vitest --ui --coverage"
}
}
すべてのファイルをカバレッジ対象にする
デフォルトでは、テストで読み込まれたファイルのみがカバレッジに含まれます。テストされていないファイルも含めてカバレッジを計測するには、all オプションを使用します。
export default defineConfig({
test: {
coverage: {
provider: 'v8',
all: true, // テストされていないファイルも含める
include: ['src/**/*.{ts,tsx}'],
exclude: [
'src/**/*.test.{ts,tsx}',
'src/**/*.spec.{ts,tsx}',
'src/**/*.d.ts',
],
},
},
})
v8 と istanbul の使い分け
| 観点 | v8 | istanbul |
|---|---|---|
| 速度 | 高速(JIT コンパイル時に計測) | やや遅い(事前インストルメントが必要) |
| 精度 | Vitest 3.2.0 以降は Istanbul と同等 | 高精度 |
| 対応環境 | V8 エンジン(Chrome、Node.js)のみ | すべての JavaScript ランタイム |
| 推奨用途 | 一般的なプロジェクト(デフォルト) | Firefox や Bun など V8 以外の環境 |
基本的には v8(デフォルト)を使用し、特定の環境でのみ istanbul を検討してください。
まとめ
この記事では、Vitest でコードカバレッジを取得する方法を解説しました。
Vitest は v8 と istanbul の2つのカバレッジプロバイダーをサポートしています。v8 はデフォルトで有効で、Vitest 3.2.0 以降は AST ベースの解析により Istanbul と同等の精度が得られます。
カバレッジを有効にするには --coverage フラグを使用し、設定ファイルでレポーター、対象ファイル、しきい値などをカスタマイズできます。Vitest UI を使用すれば、ブラウザ上で視覚的にカバレッジレポートを確認することも可能です。
CI/CD パイプラインに組み込むことで、継続的にコードの品質を監視し、チーム全体でカバレッジを維持できます。