Skip to main content
tdd-workflow Use this skill when writing new features, fixing bugs, or refactoring code. Enforces test-driven development with 80%+ coverage including unit, integration, and E2E tests.
Jump to install Skills Marketplace Discover and explore AI skills built by the community.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Copy promptShow prompt details A direct command skips the review prompt. Inspect the source before running it.
npx skills add https://github.com/linnefromice/papatune-flutter --skill tdd-workflowThe command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Download Zip Downloading... More from this repository
Related occupations SOC
Based on SOC occupation classification
name tdd-workflow description Use this skill when writing new features, fixing bugs, or refactoring code. Enforces test-driven development with 80%+ coverage including unit, integration, and E2E tests.
テスト駆動開発ワークフロー
このスキルはすべてのコード開発が包括的なテストカバレッジを伴うTDD原則に従うことを保証する。
発動タイミング
新しい機能や機能性を書く時
バグや問題を修正する時
既存コードをリファクタリングする時
APIエンドポイントを追加する時
新しいコンポーネントを作成する時
コア原則
1. コードの前にテスト
常にテストを最初に書き、次にテストを通すコードを実装する。
2. カバレッジ要件
最低80%カバレッジ(ユニット + 統合 + E2E)
すべてのエッジケースをカバー
エラーシナリオをテスト
境界条件を検証
3. テストタイプ
ユニットテスト
個々の関数とユーティリティ
コンポーネントロジック
純粋関数
ヘルパーとユーティリティ
統合テスト
APIエンドポイント
データベース操作
サービスインタラクション
外部APIコール
E2Eテスト(Playwright)
クリティカルなユーザーフロー
完全なワークフロー
ブラウザ自動化
UIインタラクション
TDDワークフローステップ
ステップ1: ユーザージャーニーを書く
[役割]として、[アクション]したい、それにより[メリット]を得る
例:
ユーザーとして、マーケットをセマンティックに検索したい、
それにより正確なキーワードなしでも関連マーケットを見つけられる。
ステップ2: テストケースを生成
各ユーザージャーニーに対して包括的なテストケースを作成:
describe ('Semantic Search' , () => {
it ('returns relevant markets for query' , async () => {
})
it ( , () => {
})
( , () => {
})
( , () => {
})
})
'handles empty query gracefully'
async
it
'falls back to substring search when Redis unavailable'
async
it
'sorts results by similarity score'
async
ステップ3: テスト実行(失敗すべき)
ステップ4: コードを実装
export async function searchMarkets (query : string ) {
}
ステップ5: テスト再実行
ステップ6: リファクタ
重複を削除
命名を改善
パフォーマンスを最適化
可読性を向上
ステップ7: カバレッジを確認
テストパターン
ユニットテストパターン(Jest/Vitest) import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from './Button'
describe ('Button Component' , () => {
it ('renders with correct text' , () => {
render (<Button > Click me</Button > )
expect (screen.getByText ('Click me' )).toBeInTheDocument ()
})
it ('calls onClick when clicked' , () => {
const handleClick = jest.fn ()
render (<Button onClick ={handleClick} > Click</Button > )
fireEvent.click (screen.getByRole ('button' ))
expect (handleClick).toHaveBeenCalledTimes (1 )
})
it ('is disabled when disabled prop is true' , () => {
render (<Button disabled > Click</Button > )
expect (screen.getByRole ('button' )).toBeDisabled ()
})
})
API統合テストパターン import { NextRequest } from 'next/server'
import { GET } from './route'
describe ('GET /api/markets' , () => {
it ('returns markets successfully' , async () => {
const request = new NextRequest ('http://localhost/api/markets' )
const response = await GET (request)
const data = await response.json ()
expect (response.status ).toBe (200 )
expect (data.success ).toBe (true )
expect (Array .isArray (data.data )).toBe (true )
})
it ('validates query parameters' , async () => {
const request = new NextRequest ('http://localhost/api/markets?limit=invalid' )
const response = await GET (request)
expect (response.status ).toBe (400 )
})
it ('handles database errors gracefully' , async () => {
const request = new NextRequest ('http://localhost/api/markets' )
})
})
E2Eテストパターン(Playwright) import { test, expect } from '@playwright/test'
test ('user can search and filter markets' , async ({ page }) => {
await page.goto ('/' )
await page.click ('a[href="/markets"]' )
await expect (page.locator ('h1' )).toContainText ('Markets' )
await page.fill ('input[placeholder="Search markets"]' , 'election' )
await page.waitForTimeout (600 )
const results = page.locator ('[data-testid="market-card"]' )
await expect (results).toHaveCount (5 , { timeout : 5000 })
const firstResult = results.first ()
await expect (firstResult).toContainText ('election' , { ignoreCase : true })
await page.click ('button:has-text("Active")' )
await expect (results).toHaveCount (3 )
})
test ('user can create a new market' , async ({ page }) => {
await page.goto ('/creator-dashboard' )
await page.fill ('input[name="name"]' , 'Test Market' )
await page.fill ('textarea[name="description"]' , 'Test description' )
await page.fill ('input[name="endDate"]' , '2025-12-31' )
await page.click ('button[type="submit"]' )
await expect (page.locator ('text=Market created successfully' )).toBeVisible ()
await expect (page).toHaveURL (/\/markets\/test-market/ )
})
テストファイル構成 src/
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.test.tsx # ユニットテスト
│ │ └── Button.stories.tsx # Storybook
│ └── MarketCard/
│ ├── MarketCard.tsx
│ └── MarketCard.test.tsx
├── app/
│ └── api/
│ └── markets/
│ ├── route.ts
│ └── route.test.ts # 統合テスト
└── e2e/
├── markets.spec.ts # E2Eテスト
├── trading.spec.ts
└── auth.spec.ts
外部サービスのモック
Supabaseモック jest.mock ('@/lib/supabase' , () => ({
supabase : {
from : jest.fn (() => ({
select : jest.fn (() => ({
eq : jest.fn (() => Promise .resolve ({
data : [{ id : 1 , name : 'Test Market' }],
error : null
}))
}))
}))
}
}))
Redisモック jest.mock ('@/lib/redis' , () => ({
searchMarketsByVector : jest.fn (() => Promise .resolve ([
{ slug : 'test-market' , similarity_score : 0.95 }
])),
checkRedisHealth : jest.fn (() => Promise .resolve ({ connected : true }))
}))
OpenAIモック jest.mock ('@/lib/openai' , () => ({
generateEmbedding : jest.fn (() => Promise .resolve (
new Array (1536 ).fill (0.1 )
))
}))
テストカバレッジ確認
カバレッジレポート実行
カバレッジ閾値 {
"jest" : {
"coverageThresholds" : {
"global" : {
"branches" : 80 ,
"functions" : 80 ,
"lines" : 80 ,
"statements" : 80
}
}
}
}
一般的なテストの間違いを避ける
❌ 間違い: 実装詳細をテスト
expect (component.state .count ).toBe (5 )
✅ 正しい: ユーザーに見える動作をテスト
expect (screen.getByText ('Count: 5' )).toBeInTheDocument ()
❌ 間違い: 脆いセレクター
await page.click ('.css-class-xyz' )
✅ 正しい: セマンティックセレクター
await page.click ('button:has-text("Submit")' )
await page.click ('[data-testid="submit-button"]' )
❌ 間違い: テスト分離なし
test ('creates user' , () => { })
test ('updates same user' , () => { })
✅ 正しい: 独立したテスト
test ('creates user' , () => {
const user = createTestUser ()
})
test ('updates user' , () => {
const user = createTestUser ()
})
継続的テスト
開発中のウォッチモード
Pre-Commitフック
CI/CD統合
- name: Run Tests
run: npm test -- --coverage
- name: Upload Coverage
uses: codecov/codecov-action@v3
ベストプラクティス
テストを最初に書く - 常にTDD
テストごとに1つのアサート - 単一の動作に焦点
説明的なテスト名 - 何がテストされるか説明
Arrange-Act-Assert - 明確なテスト構造
外部依存関係をモック - ユニットテストを分離
エッジケースをテスト - null、undefined、空、大きい値
エラーパスをテスト - ハッピーパスだけでなく
テストを高速に保つ - ユニットテストは各50ms未満
テスト後にクリーンアップ - 副作用なし
カバレッジレポートをレビュー - ギャップを特定
成功メトリクス
80%以上のコードカバレッジを達成
すべてのテストが通過(グリーン)
スキップまたは無効化されたテストなし
高速なテスト実行(ユニットテストで30秒未満)
E2Eテストがクリティカルなユーザーフローをカバー
テストが本番前にバグを検出
覚えておくこと : テストはオプションではない。自信を持ったリファクタリング、迅速な開発、本番の信頼性を可能にするセーフティネット。