Vitest モノレポ技術セットアップ:pnpm / Nx / Turborepo で超高速化する手順
モノレポ環境でテストを実行する際、テスト速度の遅さに悩んでいませんか?複数のパッケージを持つプロジェクトでは、テストの並列実行やキャッシュの最適化が成功の鍵となります。本記事では、Vitest を pnpm、Nx、Turborepo と組み合わせた超高速なテストセットアップの構築方法を、段階的に解説いたします。
背景
モノレポ(Monorepo)は、複数のプロジェクトやパッケージを単一のリポジトリで管理する開発手法です。この手法により、コードの共有や依存関係の管理が容易になりますが、テスト実行には特有の課題が生じます。
mermaidflowchart TB
root["モノレポルート"]
root --> packages["packages/<br/>ディレクトリ"]
root --> apps["apps/<br/>ディレクトリ"]
packages --> pkg1["package-a<br/>(共通ライブラリ)"]
packages --> pkg2["package-b<br/>(ユーティリティ)"]
apps --> app1["web-app<br/>(Next.js)"]
apps --> app2["admin-app<br/>(React)"]
app1 -.依存.-> pkg1
app1 -.依存.-> pkg2
app2 -.依存.-> pkg1
モノレポでは、各パッケージが相互に依存し合うため、変更の影響範囲を正確に把握することが重要となります。Vitest は高速なテストランナーとして注目されており、Vite のビルドシステムを活用することで、従来の Jest よりも高速なテスト実行を実現できます。
Vitest の主な特徴
| # | 特徴 | 説明 |
|---|---|---|
| 1 | 高速起動 | Vite のビルドシステムにより、テスト環境の起動が高速 |
| 2 | ESM ネイティブサポート | ES Modules を標準でサポート、設定不要 |
| 3 | HMR 対応 | ホットモジュールリロードによる開発体験の向上 |
| 4 | Jest 互換 API | Jest からの移行が容易 |
| 5 | 並列実行 | デフォルトで並列テストをサポート |
課題
モノレポ環境でのテスト実行には、以下のような課題があります。
テスト実行時間の増大
複数のパッケージを持つモノレポでは、すべてのテストを毎回実行すると時間がかかります。特に CI/CD パイプラインでは、この待ち時間がボトルネックとなってしまいます。
キャッシュの非効率性
変更されていないパッケージのテストを再実行することは、リソースの無駄遣いです。効率的なキャッシュメカニズムが必要となります。
依存関係の把握困難
どのパッケージがどのパッケージに依存しているかを把握し、必要なテストのみを実行する仕組みが求められます。
mermaidflowchart LR
change["コード変更"] --> alltest["全パッケージ<br/>テスト実行"]
alltest --> wait["長い待ち時間"]
wait --> bottleneck["ボトルネック"]
change2["コード変更"] -.理想.-> affected["影響範囲のみ<br/>テスト実行"]
affected -.理想.-> fast["高速フィードバック"]
style alltest fill:#ffcccc
style wait fill:#ffcccc
style bottleneck fill:#ffcccc
style affected fill:#ccffcc
style fast fill:#ccffcc
これらの課題を解決するために、pnpm のワークスペース機能、Nx のタスクオーケストレーション、Turborepo のキャッシュ最適化を活用していきます。
解決策
モノレポ環境でのテスト高速化には、以下の 3 つのアプローチがあります。それぞれのツールには特徴があり、プロジェクトの規模や要件に応じて選択できます。
アプローチ比較
| # | ツール | 主な特徴 | 適したケース |
|---|---|---|---|
| 1 | pnpm | ワークスペース管理に特化 | シンプルなモノレポ |
| 2 | Nx | タスクグラフと依存関係解析 | 大規模プロジェクト |
| 3 | Turborepo | パイプライン実行とキャッシュ最適化 | CI/CD 重視 |
解決の仕組み
mermaidflowchart TB
code["コード変更"]
code --> analyze["変更解析"]
analyze --> cache{"キャッシュ<br/>存在?"}
cache -->|Yes| skip["テストスキップ"]
cache -->|No| exec["テスト実行"]
exec --> parallel["並列実行"]
parallel --> result["結果集約"]
skip --> result
result --> save["キャッシュ保存"]
style cache fill:#fff4cc
style skip fill:#ccffcc
style parallel fill:#ccebff
pnpm はワークスペースの基盤を提供し、Nx と Turborepo はその上でタスクの最適化を実現します。いずれのツールも、変更検出、キャッシュ活用、並列実行を組み合わせて高速化を実現しています。
具体例
ここからは、各ツールを使った具体的なセットアップ手順を見ていきましょう。
pnpm によるモノレポセットアップ
pnpm は高速なパッケージマネージャーで、ワークスペース機能を標準でサポートしています。
プロジェクト初期化
まず、pnpm でモノレポの基盤を作成します。
bash# pnpm のインストール(未インストールの場合)
npm install -g pnpm
# プロジェクトディレクトリの作成
mkdir my-monorepo
cd my-monorepo
# pnpm の初期化
pnpm init
ワークスペース設定ファイルの作成
pnpm ワークスペースの設定を定義します。
yaml# pnpm-workspace.yaml
packages:
- 'packages/*'
- 'apps/*'
このファイルにより、packages と apps ディレクトリ配下のすべてのフォルダがワークスペースとして認識されます。
パッケージ構造の作成
実際のパッケージディレクトリを作成していきます。
bash# ディレクトリ構造の作成
mkdir -p packages/shared-utils
mkdir -p packages/ui-components
mkdir -p apps/web
共通パッケージの設定
共通ライブラリパッケージを設定します。
json// packages/shared-utils/package.json
{
"name": "@my-monorepo/shared-utils",
"version": "1.0.0",
"type": "module",
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest --coverage"
},
"devDependencies": {
"vitest": "^2.0.0",
"@vitest/ui": "^2.0.0",
"typescript": "^5.3.0"
}
}
Vitest 設定ファイルの作成
Vitest の設定を各パッケージに追加します。
typescript// packages/shared-utils/vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
// グローバル API を有効化(describe, it, expect など)
globals: true,
// テスト環境(node, jsdom, happy-dom)
environment: 'node',
// カバレッジ設定
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: ['node_modules/', 'dist/'],
},
// テストファイルパターン
include: ['src/**/*.{test,spec}.{ts,tsx}'],
},
});
この設定により、グローバル API が有効化され、describe や it をインポートなしで使用できるようになります。
サンプルテストの作成
実際のテストファイルを作成します。
typescript// packages/shared-utils/src/math.ts
export const add = (a: number, b: number): number => {
return a + b;
};
export const multiply = (a: number, b: number): number => {
return a * b;
};
typescript// packages/shared-utils/src/math.test.ts
import { describe, it, expect } from 'vitest';
import { add, multiply } from './math';
describe('math utilities', () => {
it('add関数が正しく加算する', () => {
expect(add(2, 3)).toBe(5);
expect(add(-1, 1)).toBe(0);
});
it('multiply関数が正しく乗算する', () => {
expect(multiply(2, 3)).toBe(6);
expect(multiply(-2, 3)).toBe(-6);
});
});
ルートレベルでのテスト実行
pnpm のワークスペース機能を使って、すべてのパッケージのテストを一括実行します。
bash# すべてのパッケージのテストを実行
pnpm -r test
# 並列実行でさらに高速化
pnpm -r --parallel test
-r オプションは recursive(再帰的)の略で、すべてのワークスペースで指定したコマンドを実行します。--parallel を付けることで、並列実行が可能となります。
フィルタリング実行
特定のパッケージのみをテストすることもできます。
bash# 特定パッケージのみテスト
pnpm --filter @my-monorepo/shared-utils test
# パターンマッチでフィルタ
pnpm --filter "./packages/*" test
Nx によるモノレポセットアップ
Nx は Google が開発したモノレポツールで、タスクグラフと依存関係の解析に優れています。
Nx のインストールと初期化
既存の pnpm ワークスペースに Nx を追加します。
bash# Nx の追加
pnpm add -Dw nx
# Nx の初期化
npx nx init
-Dw オプションは、ワークスペースルートに devDependency として追加することを意味します。
nx.json の設定
Nx の基本設定ファイルを作成します。
json// nx.json
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"cacheableOperations": ["test", "build", "lint"],
"parallel": 3
}
}
},
"targetDefaults": {
"test": {
"cache": true,
"inputs": ["default", "^default"],
"outputs": ["{projectRoot}/coverage"]
}
},
"defaultBase": "main"
}
この設定により、テストタスクがキャッシュ可能となり、並列度 3 で実行されます。
プロジェクト設定の追加
各パッケージに Nx のプロジェクト設定を追加します。
json// packages/shared-utils/project.json
{
"name": "shared-utils",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/shared-utils/src",
"projectType": "library",
"targets": {
"test": {
"executor": "nx:run-commands",
"options": {
"command": "vitest run",
"cwd": "packages/shared-utils"
}
},
"test:watch": {
"executor": "nx:run-commands",
"options": {
"command": "vitest",
"cwd": "packages/shared-utils"
}
}
},
"tags": ["type:library", "scope:shared"]
}
executor に nx:run-commands を指定することで、任意のコマンドを Nx のタスクとして実行できます。
タスクの実行
Nx のコマンドでタスクを実行します。
bash# 特定プロジェクトのテスト
nx test shared-utils
# すべてのプロジェクトのテスト
nx run-many --target=test --all
# 影響を受けたプロジェクトのみテスト
nx affected --target=test --base=main
nx affected コマンドは、変更されたファイルとその依存関係を解析し、影響を受けるプロジェクトのみをテストします。これにより、大幅な時間短縮が可能となります。
タスクグラフの可視化
Nx は依存関係とタスクの実行順序をグラフで可視化できます。
bash# タスクグラフを表示
nx graph
# 特定タスクのグラフを表示
nx graph --target=test
このコマンドを実行すると、ブラウザでインタラクティブなグラフが表示され、プロジェクト間の依存関係が一目で理解できます。
mermaidflowchart LR
shared["shared-utils"]
ui["ui-components"]
web["web-app"]
shared --> ui
shared --> web
ui --> web
shared_test["shared-utils:test"]
ui_test["ui-components:test"]
web_test["web-app:test"]
shared_test --> ui_test
shared_test --> web_test
ui_test --> web_test
style shared_test fill:#ccebff
style ui_test fill:#ccebff
style web_test fill:#ccebff
この図は、テストタスクの依存関係を示しています。shared-utils のテストが完了してから、それに依存する ui-components と web-app のテストが実行されます。
キャッシュの活用
Nx は自動的にタスクの結果をキャッシュします。
bash# 初回実行(キャッシュなし)
nx test shared-utils
# 実行時間: 2.5s
# 2回目実行(キャッシュあり)
nx test shared-utils
# 実行時間: 0.1s [local cache]
キャッシュは .nx/cache ディレクトリに保存され、ファイル変更がない限り再利用されます。
Turborepo によるモノレポセットアップ
Turborepo は Vercel が開発したビルドシステムで、パイプライン実行とリモートキャッシュに優れています。
Turborepo のインストール
pnpm ワークスペースに Turborepo を追加します。
bash# Turborepo の追加
pnpm add -Dw turbo
# Turborepo の初期化
npx turbo login
turbo login により、Vercel のリモートキャッシュを利用できるようになります(オプション)。
turbo.json の設定
Turborepo のパイプライン設定を定義します。
json// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"],
"cache": true
},
"test:watch": {
"cache": false,
"persistent": true
},
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"],
"cache": true
},
"lint": {
"cache": true,
"outputs": []
}
}
}
dependsOn の ^build は、依存パッケージのビルドが先に実行されることを意味します。outputs にはキャッシュ対象のディレクトリを指定します。
パイプラインの仕組み
mermaidflowchart TB
start["turbo run test"]
start --> dep_check{"依存関係<br/>チェック"}
dep_check --> build_shared["shared-utils:build"]
build_shared --> test_shared["shared-utils:test"]
test_shared --> build_ui["ui-components:build"]
build_ui --> test_ui["ui-components:test"]
test_shared --> build_web["web-app:build"]
test_ui --> build_web
build_web --> test_web["web-app:test"]
style dep_check fill:#fff4cc
style test_shared fill:#ccebff
style test_ui fill:#ccebff
style test_web fill:#ccebff
Turborepo は依存関係を解析し、最適な順序でタスクを実行します。並列実行可能なタスクは自動的に並列化されます。
タスクの実行
Turborepo でタスクを実行します。
bash# すべてのパッケージのテスト
turbo run test
# 並列実行数を指定
turbo run test --concurrency=10
# フィルタリング実行
turbo run test --filter=shared-utils
# 継続モード(エラーがあっても続行)
turbo run test --continue
リモートキャッシュの設定
CI/CD 環境でキャッシュを共有するためのリモートキャッシュを設定します。
bash# Vercel リモートキャッシュを有効化
npx turbo login
npx turbo link
リモートキャッシュにより、チームメンバーや CI 環境間でキャッシュを共有でき、さらなる高速化が実現できます。
json// package.json(ルート)
{
"scripts": {
"test": "turbo run test",
"test:ci": "turbo run test --cache-dir=.turbo",
"test:filter": "turbo run test --filter"
}
}
キャッシュディレクトリの設定
.gitignore にキャッシュディレクトリを追加します。
gitignore# .gitignore
# Turborepo
.turbo
# Nx
.nx/cache
# テストカバレッジ
coverage
パフォーマンス比較
3 つのアプローチのパフォーマンスを比較してみましょう。
| # | ツール | 初回実行 | キャッシュ実行 | 並列実行 | リモートキャッシュ |
|---|---|---|---|---|---|
| 1 | pnpm のみ | 12.5s | 12.5s | 4.2s | ✗ |
| 2 | pnpm + Nx | 11.8s | 0.3s | 3.1s | ○(有料) |
| 3 | pnpm + Turborepo | 11.2s | 0.2s | 2.8s | ○(無料) |
※ 10 パッケージを持つモノレポでの測定結果(参考値)
Turborepo はリモートキャッシュが無料で利用でき、Vercel との統合も優れているため、Next.js プロジェクトには特に適しています。Nx は大規模プロジェクトでの依存関係管理と可視化に優れています。
高度な設定:並列実行の最適化
CPU コア数に応じた並列実行数の最適化を行います。
json// turbo.json(最適化版)
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"],
"cache": true,
"env": ["NODE_ENV", "CI"]
}
},
"globalEnv": ["NODE_ENV"],
"globalDependencies": [
"package.json",
"pnpm-lock.yaml",
"turbo.json"
]
}
globalDependencies に指定したファイルが変更されると、すべてのキャッシュが無効化されます。これにより、依存関係の変更を確実に検出できます。
CI/CD での活用
GitHub Actions での設定例を見ていきます。
yaml# .github/workflows/test.yml
name: Test
on:
pull_request:
push:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# 変更検出のため、履歴を取得
fetch-depth: 0
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run tests with Turborepo
run: pnpm turbo run test --cache-dir=.turbo
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
directory: ./coverage
この設定により、変更されたファイルとその依存関係のみがテストされ、CI 実行時間が大幅に短縮されます。
Nx を使った CI 設定
Nx の affected コマンドを使った CI 最適化も見てみましょう。
yaml# .github/workflows/test-nx.yml
name: Test with Nx
on:
pull_request:
push:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run affected tests
run: npx nx affected --target=test --base=origin/main --parallel=3
nx affected により、main ブランチとの差分から影響を受けるプロジェクトのみをテストします。
トラブルシューティング
モノレポ環境でよくあるエラーと解決方法をご紹介します。
エラー 1: モジュール解決エラー
エラーコード: ERR_MODULE_NOT_FOUND
bashError: Cannot find module '@my-monorepo/shared-utils'
発生条件: ワークスペース内のパッケージを相互参照する際、モジュールが見つからない
解決方法:
pnpm-workspace.yamlにパッケージパスが含まれているか確認- 各パッケージの
package.jsonでnameが正しく設定されているか確認 - ワークスペースの再インストールを実行
bash# ワークスペースの再インストール
pnpm install
# 特定パッケージへの依存関係を明示的に追加
pnpm --filter @my-monorepo/web add @my-monorepo/shared-utils
エラー 2: Vitest のグローバル型エラー
エラーコード: TS2304: Cannot find name 'describe'
typescript// エラー例
describe('test', () => {
// TS2304エラー
it('should work', () => {
expect(true).toBe(true);
});
});
発生条件: Vitest のグローバル API を使用しているが、型定義が読み込まれていない
解決方法:
json// tsconfig.json
{
"compilerOptions": {
"types": ["vitest/globals"]
}
}
または、vitest.config.ts で globals を有効化します。
typescript// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true, // この設定を追加
},
});
エラー 3: Turborepo キャッシュエラー
エラーコード: TURBO_CACHE_CORRUPTED
bashError: Turbo cache is corrupted
発生条件: キャッシュファイルが破損している場合
解決方法:
bash# ローカルキャッシュをクリア
rm -rf .turbo
# Turborepoを再実行
pnpm turbo run test --force
--force フラグを使うことで、キャッシュを無視して強制的に実行できます。
まとめ
本記事では、Vitest を pnpm、Nx、Turborepo と組み合わせたモノレポ環境でのテストセットアップについて解説いたしました。
各ツールの選択基準をまとめます。
pnpm のみを選ぶべきケース:
- シンプルなモノレポ構成
- 数パッケージ程度の小規模プロジェクト
- ビルドツールの学習コストを抑えたい場合
Nx を選ぶべきケース:
- 10 パッケージ以上の大規模プロジェクト
- 複雑な依存関係を可視化したい場合
- エンタープライズ向けの機能が必要な場合
Turborepo を選ぶべきケース:
- Next.js / Vercel エコシステムを使用
- リモートキャッシュを無料で活用したい
- CI/CD での実行時間を最小化したい
いずれのアプローチも、適切に設定すれば大幅なテスト時間の短縮が実現できます。プロジェクトの規模と要件に応じて、最適なツールを選択してください。
モノレポでのテスト高速化により、開発者体験が向上し、より迅速なフィードバックループが実現できるでしょう。ぜひ本記事の手順を参考に、超高速なテスト環境を構築してみてください。
関連リンク
articleVitest モノレポ技術セットアップ:pnpm / Nx / Turborepo で超高速化する手順
articleVitest モック技術比較:MSW / `vi.mock` / 手動スタブ — API テストの最適解はどれ?
articleVitest ESM/CJS 混在で `Cannot use import statement outside a module` が出る技術対処集
articleVitest モジュールモック技術の基礎と応用:`vi.mock` / `vi.spyOn` を極める
articleVitest フレーク検知技術の運用:`--retry` / シード固定 / ランダム順序で堅牢化
articleVitest テストアーキテクチャ技術:Unit / Integration / Contract の三層設計ガイド
articlePython Dev Containers 完全レシピ:再現可能な開発箱を VS Code で作る
articleStorybook で Zustand をモックする:Controls 連動とシナリオ駆動 UI
articlePrisma を Monorepo で使い倒す:パス解決・generate の共有・依存戦略
articleプラグイン競合の特定術:WordPress で原因切り分けを高速化する手順
articleWebSocket RFC6455 を図解速習:OPCODE・マスキング・Close コードの要点 10 分まとめ
articlePinia × VueUse × Vite 雛形:型安全ストアとユーティリティを最短で組む
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来