Next.js E2E テスト:Playwright 自動化テスト実践ガイド
バグチケットに赤い「緊急」ラベル。決済フローがまた壊れた。テスト環境では問題ないのに、本番だけ動かない。
先週のリリース前、30 以上の画面を手動クリックし、十数個のフォームを入力し、3 種類のブラウザを行き来した——それでも、モバイルで最下部までスクロールしないと出ないボタンの不具合を見落とした。プロダクトマネージャーから「クーポンが使えない」と連絡が来る。
もう手動テストは限界。人海戦術は、いずれ自分もチームも疲弊させる。
選定で Cypress、Selenium、Puppeteer、Playwright を比較し、マルチブラウザ対応と設定の手軽さから Playwright を選んだ。導入 1 週間で、手動では見つからなかった 5 件のバグ——Firefox 限定のスタイル崩れや、非同期 API の競合状態など——を検出できた。
最初は設定を何度も直し、テストも 2 回書き直した。今は CI/CD で全自動、コミットのたびにテストが走り、本番バグは半分以下に減った。
なぜ Playwright か(vs Cypress)
Cypress を使ったことがあるなら、設定の簡単さ、ドキュメント、コミュニティの活発さは知っているはず。それでも最終的に Playwright にした理由は 3 つ。
マルチブラウザ対応が弱い。Cypress の Firefox / Safari 対応は長く中途半端で、実質 Chromium 中心。Chrome では完璧な決済ページが Safari で真っ白——Safari 非対応の CSS プロパティが原因だった。Playwright は Chromium、Firefox、WebKit をネイティブサポートし、1 セットのテストで主要ブラウザをカバーできる。
テスト速度。Playwright の並列実行は強い。Cypress は直列で 50 ケースに十数分、Playwright は 8 worker 並列で 5 分。CI/CD では 1 分がコストに直結する。
API 設計。Cypress から Playwright に移った当初は、チェーン呼び出しの書き心地に慣れなかった。しばらく async/await を使ううち、現代 JavaScript の書き方に近く、Next.js の Server Components とも一貫すると感じた。
Cypress が悪いわけではない。Chrome だけでよく、テストに不慣れなチームなら Cypress の方が入りやすい。デバッグツールとタイムトラベル(Time Travel)は初心者に優しい。
ただし私の要件には Playwright が合った。
- クロスブラウザテストが必要
- Next.js/React 経験があり、
async/awaitに抵抗がない - CI で速いフィードバックが欲しい
- API ルートと SSR ページもテストしたい
ツールに絶対の優劣はない。小規模でテスト経験が浅いなら Cypress で素早く始める。ある程度の規模で長期投資するなら Playwright がよい選択。
Next.js + Playwright 設定実践
Playwright のインストールは 3 コマンドで済む。
npm init playwright@latest
# または pnpm
pnpm create playwright
インストール時の質問へのおすすめ:
- TypeScript? Yes(型ヒントでミスを減らせる)
- テストディレクトリ?tests(デフォルトで OK)
- GitHub Actions? Yes(後述の CI/CD で使う)
完了すると、次のファイルが増える。
your-nextjs-project/
├── tests/ # テストケースディレクトリ
│ └── example.spec.ts
├── playwright.config.ts # Playwright 設定
└── .github/
└── workflows/
└── playwright.yml # CI 設定
設定ファイルで踏んだ坑
初期の playwright.config.ts は一般 Web 向け。Next.js では調整が必要。半年使って安定した設定は次のとおり。
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
// テストディレクトリ
testDir: './tests',
// グローバルタイムアウト:単一テスト 30 秒
timeout: 30 * 1000,
// グローバル expect タイムアウト:要素検索 5 秒
expect: {
timeout: 5000,
},
// 失敗時リトライ(CI では有効推奨)
retries: process.env.CI ? 2 : 0,
// 並列 worker 数(8 コアマシンなら 4)
workers: process.env.CI ? 2 : 4,
// テストレポート
reporter: [
['html'], // HTML レポート生成
['list'], // ターミナルリスト出力
process.env.CI ? ['github'] : ['list'], // CI では GitHub 形式
],
// Next.js 開発サーバー起動
webServer: {
command: 'npm run dev',
port: 3000,
timeout: 120 * 1000, // 初回起動のコンパイル待ち
reuseExistingServer: !process.env.CI, // ローカルは既存サーバー再利用
},
// テストプロジェクト(マルチブラウザ)
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
// モバイル(任意)
{
name: 'Mobile Chrome',
use: { ...devices['Pixel 5'] },
},
],
// グローバル設定
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry', // 失敗時 trace 記録
screenshot: 'only-on-failure', // 失敗時スクリーンショット
video: 'retain-on-failure', // 失敗時録画
},
});
よく踏む坑
-
webServer.timeoutは十分長く。最初 30 秒にしていたが、Next.js のコールドスタートでコンパイルが走り、よくタイムアウトした。120 秒で安定。 -
ローカルでは
reuseExistingServer: true。毎回 Next.js を再起動すると待ち時間が地獄になる。 -
workersは盛りすぎない。CPU コア数いっぱいにすると PC が固まった。半分くらいがバランスよい。 -
モバイルテストは任意。レスポンシブな Next.js なら Mobile Chrome でモバイル固有バグを拾えるが、時間は約 2 倍。要件次第。
設定後、公式サンプルを実行。
npx playwright test
緑の passed が出れば環境 OK。本番のテストケースに進める。
画面操作テストのベストプラクティス(Page Object Model)
最初は全部 1 ファイルに書いていた。ログインページだけ 100 行超、page.locator、page.fill、page.click が散乱。ボタンのセレクタを 1 つ変えたら、十数ファイルを直す羽目になった。
Page Object Model(POM) を学んで、コードがかなりすっきりした。ページ操作をクラスにまとめ、テストはメソッド呼び出しだけにする。
POM なし(反面教師)
// tests/login.spec.ts
import { test, expect } from '@playwright/test';
test('用户登录', async ({ page }) => {
await page.goto('/login');
// 直接操作、重複だらけ
await page.locator('input[name="email"]').fill('user@example.com');
await page.locator('input[name="password"]').fill('password123');
await page.locator('button[type="submit"]').click();
await expect(page.locator('h1')).toContainText('Dashboard');
});
test('登录失败提示', async ({ page }) => {
await page.goto('/login');
// 同じ操作をまた書く...
await page.locator('input[name="email"]').fill('wrong@example.com');
await page.locator('input[name="password"]').fill('wrongpass');
await page.locator('button[type="submit"]').click();
await expect(page.locator('.error')).toBeVisible();
});
input[name="email"] が input[id="email"] に変わったら、全テストを直すことになる。
POM でリファクタリング(推奨)
Page Object を作成。
// tests/pages/LoginPage.ts
import { Page, Locator } from '@playwright/test';
export class LoginPage {
readonly page: Page;
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;
readonly errorMessage: Locator;
readonly dashboardTitle: Locator;
constructor(page: Page) {
this.page = page;
this.emailInput = page.locator('input[name="email"]');
this.passwordInput = page.locator('input[name="password"]');
this.submitButton = page.locator('button[type="submit"]');
this.errorMessage = page.locator('.error');
this.dashboardTitle = page.locator('h1');
}
// ログイン操作
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
// ナビゲーション
async goto() {
await this.page.goto('/login');
}
// 検証
async expectLoginSuccess() {
await this.dashboardTitle.waitFor();
await expect(this.dashboardTitle).toContainText('Dashboard');
}
async expectLoginError() {
await expect(this.errorMessage).toBeVisible();
}
}
テストはこう短くなる。
// tests/login.spec.ts
import { test } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';
test('用户登录', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('user@example.com', 'password123');
await loginPage.expectLoginSuccess();
});
test('登录失败提示', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('wrong@example.com', 'wrongpass');
await loginPage.expectLoginError();
});
セレクタ変更は LoginPage.ts だけ。テストは自然言語のように読める。
実プロジェクトのディレクトリ構成
tests/
├── pages/ # Page Objects
│ ├── LoginPage.ts
│ ├── DashboardPage.ts
│ └── CheckoutPage.ts
├── fixtures/ # テストデータとヘルパー
│ └── testData.ts
├── auth.spec.ts # 認証テスト
├── checkout.spec.ts # 決済フローテスト
└── dashboard.spec.ts # Dashboard テスト
踏んだ坑とアドバイス
-
過度な抽象化は不要。1 回しか測らないページは直接書いてよい。POM のための POM は避ける。
-
メソッド名は意味が伝わるように。
fillLoginForm()はfillForm()より半年後も読みやすい。 -
待機ロジックは Page Object 内へ。Playwright の auto-wait は優秀だが、
waitFor()が必要なときもある。テスト側を汚さない。 -
テストデータは別管理。ユーザー名・パスワードは
fixtures/testData.tsに集約。
// tests/fixtures/testData.ts
export const testUsers = {
validUser: {
email: 'user@example.com',
password: 'password123'
},
invalidUser: {
email: 'wrong@example.com',
password: 'wrongpass'
}
};
テストでの利用例。
import { testUsers } from './fixtures/testData';
await loginPage.login(testUsers.validUser.email, testUsers.validUser.password);
このパターンで保守コストは大きく下がる。今は Page Object を定義し、数行のテストを書く——それで済む。
API ルートの E2E テスト
Next.js の API Routes もアプリの一部。Postman で手動テストする日々は終わりにした。Playwright 内で API まで測れば、ブラウザを開かなくてよい。
Playwright の request で HTTP を直接送れ、API Routes のテストに向く。
基本的な API テスト
ユーザー一覧 API の例。
// tests/api/users.spec.ts
import { test, expect } from '@playwright/test';
test.describe('用户 API 测试', () => {
test('GET /api/users - 获取用户列表', async ({ request }) => {
const response = await request.get('/api/users');
// ステータスコード
expect(response.status()).toBe(200);
// レスポンス形式
const users = await response.json();
expect(Array.isArray(users)).toBeTruthy();
expect(users.length).toBeGreaterThan(0);
// データ構造
expect(users[0]).toHaveProperty('id');
expect(users[0]).toHaveProperty('email');
expect(users[0]).toHaveProperty('name');
});
test('POST /api/users - 创建用户', async ({ request }) => {
const newUser = {
email: 'test@example.com',
name: 'Test User',
password: 'password123'
};
const response = await request.post('/api/users', {
data: newUser
});
expect(response.status()).toBe(201);
const createdUser = await response.json();
expect(createdUser.email).toBe(newUser.email);
expect(createdUser).not.toHaveProperty('password'); // パスワードは返さない
});
test('POST /api/users - 邮箱重复应返回错误', async ({ request }) => {
const duplicateUser = {
email: 'existing@example.com',
name: 'Duplicate User',
password: 'password123'
};
const response = await request.post('/api/users', {
data: duplicateUser
});
expect(response.status()).toBe(400);
const error = await response.json();
expect(error.message).toContain('邮箱已存在');
});
});
認証付き API テスト
多くの API はログイン後のみ。先に token を取り、ヘッダーに付ける。
// tests/api/auth.spec.ts
import { test, expect } from '@playwright/test';
let authToken: string;
test.describe('需要认证的 API', () => {
// 全テスト前にログイン
test.beforeAll(async ({ request }) => {
const response = await request.post('/api/auth/login', {
data: {
email: 'user@example.com',
password: 'password123'
}
});
const { token } = await response.json();
authToken = token;
});
test('GET /api/profile - 获取用户资料', async ({ request }) => {
const response = await request.get('/api/profile', {
headers: {
'Authorization': `Bearer ${authToken}`
}
});
expect(response.status()).toBe(200);
const profile = await response.json();
expect(profile.email).toBe('user@example.com');
});
test('未登录访问应返回 401', async ({ request }) => {
const response = await request.get('/api/profile');
expect(response.status()).toBe(401);
});
});
ハイブリッド:画面 + API
画面テストと API テストを組み合わせる。記事投稿フローの例。
// tests/posts.spec.ts
import { test, expect } from '@playwright/test';
test('发布文章完整流程', async ({ page, request }) => {
// 1. 画面でログイン
await page.goto('/login');
await page.fill('input[name="email"]', 'author@example.com');
await page.fill('input[name="password"]', 'password123');
await page.click('button[type="submit"]');
// 2. 編集ページへ
await page.goto('/posts/new');
await page.fill('input[name="title"]', '测试文章标题');
await page.fill('textarea[name="content"]', '这是测试内容');
await page.click('button:has-text("发布")');
// 3. 詳細ページへ遷移待ち
await page.waitForURL(/\/posts\/\d+/);
// 4. API で作成を確認
const url = page.url();
const postId = url.split('/').pop();
const response = await request.get(`/api/posts/${postId}`);
expect(response.status()).toBe(200);
const post = await response.json();
expect(post.title).toBe('测试文章标题');
expect(post.content).toBe('这是测试内容');
expect(post.status).toBe('published');
});
フロント操作とバックエンドデータの両方を検証できる。一度、「公開成功」と表示されているのに DB 上は draft のまま——状態更新ロジックのバグをこのパターンで見つけた。
実践アドバイス
-
境界値もカバー。正常系だけでなく、パラメータ欠落、型エラー、権限不足も。
-
テストデータのクリーンアップ。API テストは DB に書き込む。
afterAllで片付け。専用 DB を定期クリアする方法も。
test.afterAll(async ({ request }) => {
await request.delete('/api/test/cleanup');
});
-
外部サービスは Mock。決済・SMS などは Mock しないと、テストのたびに課金される。
-
応答時間にも目を。Playwright で所要時間を測り、アサーションを足す。
const start = Date.now();
await request.get('/api/users');
const duration = Date.now() - start;
expect(duration).toBeLessThan(1000); // 1 秒以内
API と画面を組み合わせれば、およそ 90% をカバーできる。残り 10% は単体テストで補う。
GitHub Actions CI/CD 統合
テストが書けたら CI/CD へ。コミットのたびに自動実行——何度も「大丈夫」と思って CI が赤になった経験がある。
npm init playwright は GitHub Actions 設定も生成するが、デフォルトは基本的。実運用向けに調整した例を紹介する。
基本 CI 設定
初期 .github/workflows/playwright.yml:
name: Playwright Tests
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
使えるが、課題もある。
- 毎回ブラウザをインストール——遅い
- テスト DB がない——API テストが落ちる
- レポートはダウンロードしないと見られない
本番級設定
キャッシュ、DB、レポートデプロイを足した実運用版。
name: E2E Tests
on:
push:
branches: [ main, dev ]
pull_request:
branches: [ main ]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
services:
# テスト DB(PostgreSQL)
postgres:
image: postgres:15
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
env:
DATABASE_URL: postgresql://test:test@localhost:5432/testdb
NODE_ENV: test
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: Cache Playwright browsers
uses: actions/cache@v3
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }}
- name: Install Playwright Browsers
run: npx playwright install --with-deps chromium
- name: Run database migrations
run: npm run db:migrate
- name: Run Playwright tests
run: npx playwright test
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 30
# main なら GitHub Pages にレポート
- name: Deploy report to GitHub Pages
if: always() && github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./playwright-report
設定の要点
-
services:PostgreSQL をテスト DB に。MySQL なら
mysql:8に差し替え。 -
ブラウザキャッシュ:
actions/cacheで Playwright ブラウザを保存。初回は遅いが 2 回目以降は速い。CI ではchromiumのみ——3 ブラウザは遅すぎる。 -
DB マイグレーション:テスト前に
npm run db:migrate。package.jsonに例:
{
"scripts": {
"db:migrate": "prisma migrate deploy"
}
}
- レポートデプロイ:main のレポートを GitHub Pages へ。チームがオンラインで確認できる。
環境変数
API キーなどは GitHub Secrets へ。
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
STRIPE_SECRET_KEY: ${{ secrets.STRIPE_TEST_KEY }}
失敗時のデバッグ
CI が赤になったとき、Playwright の機能が効く。
-
trace を見る:
trace: 'on-first-retry'で失敗時に trace 生成。npx playwright show-trace trace.zipで再生。 -
スクリーンショットと動画:失敗時に自動保存。画面状態が一目でわかる。
-
ローカルで CI 再現:
actで GitHub Actions をローカル実行。
# act インストール
brew install act # macOS
# または
choco install act # Windows
# workflow 実行
act -j test
踏んだ坑
-
タイムアウトは現実的に。30 分にしていたが、ハングで CI 時間を無駄にした。60 分にしつつ、遅いテストを監視。
-
並列は控えめ。CI マシンは性能が限られる。worker 2 本で十分なことが多い。
-
リトライは盛りすぎない。
retries: 2はネットワーク揺れ用。本当に壊れたテストは何度リトライしても時間の無駄。
CI 導入後、コード品質は明らかに上がった。PR は緑チェック必須——テストを軽視できなくなった。
テストカバレッジとレポート
実行後は結果確認が重要。Playwright のレポートは情報量が多く、見やすい。
HTML レポート(最常用)
npx playwright show-report
ローカルで Web ページが開き、次が確認できる。
- 各テストの pass/fail
- 実行時間
- 失敗時のスクリーンショットと動画
- Trace(実行全体を再生)
Trace Viewer が特に強い。失敗テストをクリックすると、各ステップのネットワーク、DOM スナップショット、コンソールログがタイムラインで見える。問題箇所の特定が速い。
テストカバレッジ
E2E のカバレッジはコードではなく機能カバレッジ。チェックリストで管理している。
## 测试覆盖清单
### 用户认证
- [x] 登录(正常流程)
- [x] 登录失败(错误密码)
- [x] 注册
- [x] 找回密码
- [ ] 第三方登录(Google)
### 商品管理
- [x] 添加商品
- [x] 编辑商品
- [x] 删除商品
- [ ] 批量导入
### 订单流程
- [x] 加购物车
- [x] 结算
- [x] 支付(模拟环境)
- [ ] 退款流程
tests/README.md に置き、機能追加のたびに更新。未カバーが一目でわかる。
コードカバレッジ(任意)
どうしてもコードカバレッジが見たいなら Istanbul や v8 coverage を設定できる。
// playwright.config.ts
export default defineConfig({
use: {
// コードカバレッジ有効化
trace: 'on',
// カバレッジ収集コード注入
contextOptions: {
recordVideo: {
dir: 'test-results/videos'
}
}
}
});
E2E でコードカバレッジを見ることは少ない。ロジックは単体テスト、E2E はフローが通るかを見る。
カスタムレポート
Slack や DingTalk へ結果通知も可能。カスタム reporter の例。
// my-reporter.ts
import { Reporter } from '@playwright/test/reporter';
class SlackReporter implements Reporter {
onEnd(result) {
const passed = result.suites.filter(s => s.ok).length;
const failed = result.suites.length - passed;
// Slack へ送信
fetch('https://hooks.slack.com/services/YOUR_WEBHOOK', {
method: 'POST',
body: JSON.stringify({
text: `测试完成:${passed} 通过,${failed} 失败`
})
});
}
}
export default SlackReporter;
設定で有効化。
// playwright.config.ts
export default defineConfig({
reporter: [
['html'],
['./my-reporter.ts']
]
});
テストトレンド
Playwright Test Runner に trace を上げれば、合格率や実行時間の推移を可視化できる。
私はもっと単純に、CI 後に合格率と時間を CSV に追記し、Google Sheets でグラフ化している。実用的で十分。
レポートの使い分け
-
ローカル開発:ターミナル出力。失敗時は
--debugで再実行。npx playwright test --debug -
PR レビュー:CI の HTML レポート。失敗テストと実行時間に注目。常にタイムアウトするテストはコード側の問題の可能性。
-
定期レビュー:週 1 回、カバレッジチェックリストを見て不足を補う。
レポートは数字以上に、問題発見とプロセス改善の道具。
まとめ
半年前、手動テストで深夜 3 時まで粘った夜を思い出す。今はだいぶ楽になった。
Playwright + Next.js は一度設定すればあとは楽。コミットのたび CI が走り、リリース前の不安が減った。本番バグは減り、PM からの連絡も減った(笑)。
Next.js をまだ手動テストしているなら:
- コアフローから。ログイン、決済など重要パスを先に。
- Page Object Model を使う。最初は面倒でも、長期では時間を節約。
- CI/CD に載せる。自動化テストを自動実行しないなら、書く意味が薄い。
- 100% カバレッジを追わない。コアを押さえれば十分。
E2E テストは技術ツールであると同時に、チームの協働の仕組み。品質意識が上がり、コミュニケーションコストが下がり、リリースが予測可能になる。
今は 17:30 に帰れる。空いた時間で、期限切れ間近のジム会員券を使い始めた——以前は更新する暇もなかった。
テストを書き始めよう。未来の自分が感謝してくれる。
FAQ
Playwright と Cypress、結局どっちを選ぶ?
• Playwright:クロスブラウザ(Chromium/Firefox/WebKit)、並列が速い(8 worker で Cypress の約 3 倍)、async/await 構文、中〜大規模向き
• Cypress:Chrome 中心、タイムトラベルデバッグが強い、コミュニティ資産が豊富、初心者向き
Chrome だけでよく、テスト経験が浅いなら Cypress。クロスブラウザ・CI の速度・React/Next.js 経験があるなら Playwright が向きます。
Page Object Model は必ず使うべき?
小規模(10 ケース未満)や一度きりのページなら、テスト内に直接書いても問題ありません。ただし次のような場合は POM の価値が出ます。
• 同じページを複数テストで触る
• 複数人でテストコードを保守する
• 長期運用するプロジェクト
セレクタを 1 回変えたとき——POM がないと 10 ファイル以上、あれば 1 ファイルだけです。
CI 環境でテストがいつもタイムアウトする
• webServer.timeout が短い:120 秒に(Next.js のコールドスタートでコンパイルが走る)
• worker が多すぎる:CI マシンは性能が限られるので 2〜4 で十分
• テスト自体の問題:trace でネットワーク遅延か要素待ちか切り分け
• ブラウザインストールが遅い:actions/cache でブラウザをキャッシュ
CI では chromium のみ、ローカルでマルチブラウザ——これだけでかなり速くなります。
テストデータはどう管理する?毎回 DB を手動で片付ける?
• 専用テスト DB:テスト専用、定期クリア、開発環境に影響しない
• テスト後にクリーンアップ:test.afterAll() で API を呼ぶが、漏れやすい
• Docker コンテナ:実行のたびに新しい DB、終了後に破棄(最もクリーンだが遅い)
筆者は 1 + 2 の併用。CI は Docker DB、ローカルは専用テスト DB + afterAll クリーンアップです。
API テストで第三者サービスを Mock すべき?
• コスト:本物の決済/ SMS API は都度課金
• 速度:外部応答が遅いとテスト全体が遅くなる
• 安定性:外部障害で自分のテストが落ちるべきではない
Playwright はネットワークインターセプトで Mock 可能:
await page.route('**/api/payment', route => route.fulfill({ status: 200, body: '{"success": true}' }));
Next.js API Routes で環境変数を見て、テスト時はモックデータを返す方法もあります。
テストカバレッジはどの程度が合格ライン?
優先度:
• コア(ログイン、決済、注文):100%
• 高頻度(商品閲覧、カート追加):80% 以上
• 低頻度(パスワード再設定、返金):50% 以上
• エッジ(テーマ切替、言語切替):任意
100% 追求は不要。コアフローを 100%、全体機能 60% でも 90% のバグは防げます。
Playwright があれば単体テストは不要?
• E2E(Playwright):機能フロー、UI 操作、前後端連携——遅いが網羅的
• 単体(Jest/Vitest):関数ロジック、境界値、エラー処理——速いが局所的
理想比率は単体 70%、E2E 30%。ユーティリティ、hooks、コンポーネントは単体、ユーザーフロー全体は E2E。
単体で原因を絞り、E2E で本当に動くか確認。両方やりましょう。
5分で読めます · 公開日: 2026年1月7日 · 更新日: 2026年6月8日
Next.js 完全ガイド
検索からこのページに来た場合は、前後の記事もあわせて読むと同じテーマの理解がかなり早く深まります。
前の記事
Next.js Error Boundary 完全ガイド:ランタイムエラーを上手に処理する 5 つのポイント
error.tsx の使い方、グローバルエラー処理、Server Components の例外の分け方、復旧の仕組みまで。Next.js の Error Boundary で白画面を防ぎ、ユーザー体験を守る実装を解説します。
第 31 / 47 記事
次の記事
Next.js Eコマース実践:カートと Stripe 決済の完全実装ガイド
Zustand + Stripe を使用して Eコマースのカートと決済システムを構築する方法をステップバイステップで解説します。状態管理の選定、Checkout Session の作成、Webhook による注文処理の完全なフローを網羅し、すぐに使えるコード例を提供します。
第 33 / 47 記事
関連記事
Next.js App Router 入門ガイド:コア概念と基本操作を解説
Next.js App Router 入門ガイド:コア概念と基本操作を解説
Next.js 15 実践:週末で本番級ブログシステムを構築した方法
Next.js 15 実践:週末で本番級ブログシステムを構築した方法
Next.js Middleware 実践ガイド:パスマッチ、Edge Runtime 制限とよくある落とし穴
コメント
GitHubアカウントでログインしてコメントできます