取り組みの背景:なぜAntigravity × TDDなのか
「AIに書いてもらったコードは動くが、リグレッションが怖い」——そう感じたことはないでしょうか。Antigravityをはじめとするコーディングアシスタントは、驚くほどの速度でコードを生成してくれます。しかし、テスト無しで高速に積み上げたコードベースは、やがて変更のたびに壊れる「砂の城」になりかねません。
テスト駆動開発(TDD)は、この問題に対するもっとも根本的な解答です。そしてAntigravityは、TDDの最大の障壁だった「テストを書く手間」を劇的に下げてくれます。両者を組み合わせると、「高速×安全」という一見矛盾する二つの目標を同時に達成できます。
ここではAntigravityとともに本物のTDDを実践するための、ゼロからプロダクション品質までの完全ワークフローをお伝えします。環境セットアップから始まり、ユニット・統合・E2Eテストの設計パターン、そしてCI/CDへの統合まで、実際に手を動かせる内容でまとめましました。
対象読者
この記事は以下に当てはまる方に最適です。
- Antigravityをすでに使っているが、テスト戦略が曖昧なまま開発を進めている
- TDDに興味はあるが、AIとどう組み合わせればよいかわからない
- テストを書いているが、カバレッジが低く本番障害が続いている
- ソロまたは小チームで品質と開発速度を両立したい
第1章:TDDとAntigravityの相性が抜群な理由
従来のTDDの課題
TDDの理念——「先にテストを書き、そのテストを通すコードを書く」——は美しいものの、実践では多くの開発者がつまずきます。その主な理由として、テストコードそのものを書く時間的コスト、テストのセットアップ(モック、フィクスチャ)の複雑さ、テスト設計のスキル不足が挙げられます。
AntigravityがTDDを変える
Antigravityは以下の点でTDDのボトルネックを解消します。
1. テストコード自動生成
対象となるビジネスロジックの仕様を自然言語で説明するだけで、Antigravityはテストケースを提案してくれます。エッジケースや境界値のテストも漏れなく生成できます。
2. モック・スタブの自動構築
依存関係の複雑なモジュールのモック作成は、初学者の大きな壁です。Antigravityはコードの依存グラフを解析し、適切なモック構造を提案します。
3. リファクタリング時の安全網
既存コードをAntigravityでリファクタリングする際、テストスイートがあれば「壊してしまった」瞬間にすぐ検知できます。AntigravityのAgent機能はテスト失敗を検知して自動的に修正を試みます。
TDDサイクルのAntigravity統合フロー
従来のRed→Green→Refactorサイクルに、Antigravityを組み込むと次のようになります。
1. [Red] 仕様をAntigravityに説明 → テストコードを生成 → テスト失敗を確認
2. [Green] Antigravityに「このテストを通す最小限の実装を書いて」と依頼
3. [Refactor] Antigravityに「SOLID原則に従ってリファクタして」と依頼 → テスト再実行
このサイクルを回すことで、「テストに守られた高品質なコード」が自然と積み上がっていきます。
第2章:環境セットアップ — テスト基盤の構築
必要なツールスタック
Next.js / React プロジェクトを前提に、以下の構成を推奨します。
# ユニット・統合テスト
npm install -D vitest @vitest/ui jsdom @testing-library/react @testing-library/user-event
# APIルートのテスト
npm install -D supertest @types/supertest
# E2Eテスト
npm install -D @playwright/test
npx playwright install
# カバレッジレポート
npm install -D @vitest/coverage-v8vitest.config.ts の設定
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import path from 'path'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./src/test/setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
// カバレッジ閾値(80%未満でCIを失敗させる)
thresholds: {
lines: 80,
functions: 80,
branches: 75,
statements: 80,
},
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
})Antigravityへの最初の依頼
環境を整えたら、Antigravityに以下のように依頼してみましょう。
このプロジェクトのテスト環境(vitest + React Testing Library)が整いました。
src/lib/pricing.ts の getPriceInJPY 関数のユニットテストを
TDDスタイルで作成してください。
エッジケース(0円、負の値、小数点)も含めてください。
Antigravityは関数の実装を読み込み、包括的なテストスイートを生成します。
第3章:ユニットテスト — ビジネスロジックの堅牢化
Antigravityを使ったテスト生成の実例
例として、記事のフィルタリングロジックをテストする場面を見てみましょう。
まずAntigravityに依頼します:「src/lib/articles.ts の filterArticlesByCategory 関数に対するVitest テストを書いてください。カテゴリ一致・不一致・空配列・undefinedカテゴリのケースを含めてください。」
生成されるテストは以下のようになります。
// src/lib/articles.test.ts
import { describe, it, expect } from 'vitest'
import { filterArticlesByCategory } from './articles'
import type { ArticleMeta } from '@/types'
const mockArticles: ArticleMeta[] = [
{ slug: 'intro-a2a', category: 'agents', premium: false, title: 'A2A入門', date: '2026-04-01' },
{ slug: 'custom-mcp', category: 'integrations', premium: true, title: 'MCPサーバー構築', date: '2026-04-01' },
{ slug: 'tdd-guide', category: 'tips', premium: true, title: 'TDD実践', date: '2026-04-01' },
]
describe('filterArticlesByCategory', () => {
it('指定カテゴリの記事のみ返す', () => {
const result = filterArticlesByCategory(mockArticles, 'agents')
expect(result).toHaveLength(1)
expect(result[0].slug).toBe('intro-a2a')
})
it('存在しないカテゴリは空配列を返す', () => {
const result = filterArticlesByCategory(mockArticles, 'nonexistent')
expect(result).toHaveLength(0)
})
it('空配列を渡すと空配列を返す', () => {
const result = filterArticlesByCategory([], 'agents')
expect(result).toHaveLength(0)
})
it('undefinedカテゴリは全記事を返す', () => {
const result = filterArticlesByCategory(mockArticles, undefined)
expect(result).toHaveLength(mockArticles.length)
})
})このテストがすべてパスしたら、実装は正しく動いていると確信できます。
モックの自動生成テクニック
外部依存(APIコール・DBアクセス)があるモジュールのテストでは、Antigravityにこう依頼します:「src/lib/stripe.ts のStripe APIをモック化して、createCheckoutSession のユニットテストを書いてください。成功・失敗・タイムアウトのケースを含めてください。」
// src/lib/stripe.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { createCheckoutSession } from './stripe'
// Stripeモジュール全体をモック
vi.mock('stripe', () => ({
default: vi.fn().mockImplementation(() => ({
checkout: {
sessions: {
create: vi.fn(),
},
},
})),
}))
import Stripe from 'stripe'
describe('createCheckoutSession', () => {
let mockCreate: ReturnType<typeof vi.fn>
beforeEach(() => {
const stripeInstance = new (Stripe as any)('test_key')
mockCreate = stripeInstance.checkout.sessions.create
})
it('正常系: セッションIDを返す', async () => {
mockCreate.mockResolvedValueOnce({ id: 'cs_test_123', url: 'https://checkout.stripe.com/...' })
const result = await createCheckoutSession({ priceId: 'price_test', locale: 'ja' })
expect(result.id).toBe('cs_test_123')
expect(mockCreate).toHaveBeenCalledWith(
expect.objectContaining({ mode: 'payment' })
)
})
it('異常系: Stripeエラーを適切に伝播する', async () => {
mockCreate.mockRejectedValueOnce(new Error('Invalid API Key'))
await expect(createCheckoutSession({ priceId: 'price_test', locale: 'ja' }))
.rejects.toThrow('Invalid API Key')
})
})第4章:統合テスト — コンポーネントとAPIの検証
React Testing Libraryによるコンポーネントテスト
UIコンポーネントのテストでは、実装の詳細ではなくユーザーの操作と結果に着目する点が肝心です。Antigravityを使うと、このアプローチを自然と習慣化できます。
依頼例:「MembershipCTA コンポーネントのテストを書いてください。会員・非会員それぞれの表示確認と、ボタンクリック時の動作(/api/checkout へのPOST)のテストを含めてください。」
// src/components/MembershipCTA.test.tsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { vi } from 'vitest'
import MembershipCTA from './MembershipCTA'
// fetch のモック
global.fetch = vi.fn()
describe('MembershipCTA', () => {
beforeEach(() => {
vi.clearAllMocks()
})
describe('非会員ユーザー', () => {
it('「プレミアム会員になる」ボタンが表示される', () => {
render(<MembershipCTA isPremium={false} />)
expect(screen.getByRole('button', { name: /プレミアム/i })).toBeInTheDocument()
})
it('ボタンクリックで /api/checkout に POST される', async () => {
const mockFetch = vi.mocked(fetch)
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => ({ url: 'https://checkout.stripe.com/session_test' }),
} as Response)
render(<MembershipCTA isPremium={false} />)
fireEvent.click(screen.getByRole('button', { name: /プレミアム/i }))
await waitFor(() => {
expect(mockFetch).toHaveBeenCalledWith('/api/checkout', expect.objectContaining({
method: 'POST',
}))
})
})
})
describe('会員ユーザー', () => {
it('メンバーシップ案内ではなく感謝メッセージが表示される', () => {
render(<MembershipCTA isPremium={true} />)
expect(screen.getByText(/ありがとうございます/i)).toBeInTheDocument()
expect(screen.queryByRole('button', { name: /プレミアム/i })).not.toBeInTheDocument()
})
})
})APIルートのテスト(Supertest + Vitest)
Next.js App RouterのAPIルートを直接テストするには、Supertestが便利です。
// src/app/api/checkout/route.test.ts
import { describe, it, expect, vi } from 'vitest'
import { POST } from './route'
import { NextRequest } from 'next/server'
vi.mock('@/lib/stripe', () => ({
createCheckoutSession: vi.fn().mockResolvedValue({
url: 'https://checkout.stripe.com/test',
}),
}))
describe('POST /api/checkout', () => {
it('正常: セッションURLを返す', async () => {
const request = new NextRequest('http://localhost:3000/api/checkout', {
method: 'POST',
body: JSON.stringify({ priceId: 'price_test_premium', planType: 'premium' }),
headers: { 'Content-Type': 'application/json' },
})
const response = await POST(request)
const data = await response.json()
expect(response.status).toBe(200)
expect(data.url).toContain('checkout.stripe.com')
})
it('異常: priceId未指定は400エラー', async () => {
const request = new NextRequest('http://localhost:3000/api/checkout', {
method: 'POST',
body: JSON.stringify({}),
headers: { 'Content-Type': 'application/json' },
})
const response = await POST(request)
expect(response.status).toBe(400)
})
})第5章:E2Eテスト — 実際のユーザー体験を守る
Playwrightによる重要フローの自動化
E2EテストはUIが絡む重要なユーザーフローに絞って書くのがポイントです。すべての画面に書くとメンテナンスコストが跳ね上がるため、Antigravityには「最も重要なフローを3つ挙げてください」と相談してから始めましょう。
典型的な重要フローとして、記事閲覧フロー(一覧→詳細)、プレミアム記事のペイウォール表示フロー、言語切替フロー(日英)が挙げられます。
// e2e/article-flow.spec.ts
import { test, expect } from '@playwright/test'
test.describe('記事閲覧フロー', () => {
test('一覧から詳細ページへ遷移できる', async ({ page }) => {
await page.goto('/')
// 最初の記事カードをクリック
const firstArticle = page.locator('[data-testid="article-card"]').first()
const articleTitle = await firstArticle.locator('h2').textContent()
await firstArticle.click()
// 詳細ページのタイトルが一致するか確認
await expect(page.locator('h1')).toContainText(articleTitle!)
await expect(page).toHaveURL(/\/articles\//)
})
test('プレミアム記事はペイウォールが表示される(非会員)', async ({ page }) => {
// クッキーなし(非会員状態)でプレミアム記事にアクセス
await page.goto('/articles/tips/antigravity-tdd-test-driven-development-mastery')
// ペイウォールの表示を確認
await expect(page.locator('[data-testid="paywall"]')).toBeVisible()
await expect(page.locator('[data-testid="paywall"]')).toContainText('プレミアム')
})
test('英語版に切り替えると英語コンテンツが表示される', async ({ page }) => {
await page.goto('/articles/tips/antigravity-ai-pair-programming-workflow-guide')
// 言語切替ボタンをクリック
await page.click('[data-testid="lang-switch-en"]')
await expect(page).toHaveURL(/\/en\/articles\//)
// HTMLのlang属性確認
await expect(page.locator('html')).toHaveAttribute('lang', 'en')
})
})Antigravityを使ったPlaywrightテスト生成
Antigravityに既存ページのHTMLを見せながら「このページの重要なインタラクションをテストするPlaywrightコードを書いてください」と依頼すると、data-testid の追加提案も含めて適切なテストを生成してくれます。
第6章:カバレッジ戦略 — 何を・どこまでテストすべきか
カバレッジ閾値の設定指針
「カバレッジ100%を目指すべきか」——これはよくある議論です。答えはNOです。Antigravityと一緒に開発する場合、以下の指針が現実的です。
- ビジネスロジック(
src/lib/): 90%以上を目標 - APIルート(
src/app/api/): 85%以上を目標 - UIコンポーネント(
src/components/): 70%以上を目標(純粋な表示ロジックに集中) - ページコンポーネント(
src/app/): E2Eテストで補完
Antigravityへの依頼:「カバレッジレポートの結果を添付します。カバレッジが低い箇所のテストを優先的に書いてください。ただし、単純なgetterや定数ファイルは除外してください。」
カバレッジレポートの確認コマンド
# カバレッジ付きでテスト実行
npm run test:coverage
# HTMLレポートをブラウザで確認(coverage/index.html)
npx vite preview --outDir coverage --base /
# 期待する出力例:
# ✓ src/lib/articles.test.ts (12 tests) 234ms
# ✓ src/lib/pricing.test.ts (8 tests) 123ms
# ✓ src/components/MembershipCTA.test.tsx (6 tests) 89ms
# Coverage: Lines 84.2% | Functions 87.5% | Branches 76.3%第7章:CI/CDパイプラインへの統合
GitHub Actionsでの自動テスト実行
プッシュのたびに全テストを実行し、失敗したらマージをブロックする設定を作りましょう。Antigravityに「このプロジェクト用のGitHub Actions CI設定を作ってください。vitest + playwright を実行し、カバレッジレポートをアーティファクトとして保存してください」と依頼します。
# .github/workflows/ci.yml
name: CI — Test & Quality Gate
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
unit-integration:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit & integration tests
run: npm run test:coverage
- name: Upload coverage report
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-report
path: coverage/
- name: Coverage threshold check
run: |
LINES=$(cat coverage/coverage-summary.json | jq '.total.lines.pct')
echo "Line coverage: ${LINES}%"
if (( $(echo "$LINES < 80" | bc -l) )); then
echo "❌ Coverage below threshold (80%)"
exit 1
fi
echo "✅ Coverage OK"
e2e:
runs-on: ubuntu-latest
needs: unit-integration
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci && npx playwright install --with-deps
- name: Build application
run: npm run build
- name: Run E2E tests
run: npx playwright test
env:
BASE_URL: http://localhost:3000
- name: Upload Playwright report
uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/既存のAntigravity × GitHub Actions連携については、AntigravityとGitHub Actionsによる高度なCI/CDパイプライン構築も参考にしてください。
第8章:Antigravityを使ったテストのリファクタリング
テストコードも「作品」として磨く
テストコードが増えると、今度は「テストのメンテナンス」が課題になります。Antigravityはここでも力を発揮します。
重複排除の依頼例:「このテストファイルに重複しているセットアップコードをbeforeEach/afterEachにまとめてください。」
命名改善の依頼例:「テスト名が曖昧です。it('works correctly') のような名前を、失敗時に何が問題かわかるように改善してください。」
テストの独立性チェック:「このテストスイートに順序依存(テストAが失敗するとテストBも落ちる)はありますか?修正案を提示してください。」
テスト品質を高めるAntigravityプロンプト集
# テスト網羅性レビュー
以下の関数 [関数名] のテストを確認してください。
見落としているエッジケースや異常系があれば指摘してください。
# フレイキーテストの修正
このE2Eテストが断続的に失敗します。タイミング問題を修正してください。
エラー: [エラーメッセージ]
# パフォーマンステスト
この関数は100万件のデータを処理します。
100ms以内に完了するかVitest benchmarkで測定するコードを書いてください。
VRTとの組み合わせについては、AntigravityによるVRT(ビジュアルリグレッションテスト)自動化で詳しく解説しています。
まとめ
AntigravityとTDDを組み合わせることで、「高速開発」と「品質保証」を両立する道が開けます。本記事のポイントをまとめると、以下の通りです。
まず、Antigravityはテストコードの生成・モック作成・エッジケースの列挙を自動化し、TDDの最大の障壁を取り除いてくれます。次に、ユニット→統合→E2Eという層構造でテストを設計し、各層でAntigravityを活用することで網羅性と保守性の両立が図れます。そして、GitHub Actions等のCI/CDと連携したカバレッジゲートを設けることで、品質の退化を自動的に検知できます。
テストは「書かされるもの」ではなく、「安心してコードを変える自由を与えてくれるもの」です。Antigravityがその負担を大きく引き受けてくれる今、TDDを実践しない理由はありません。
TDDの思想的背景から実践パターンまでを網羅した、この分野の必読書です。