[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6G2F98BVMEDD2Y7617FC447":3,"skill-same:01M6G2F98BVMEDD2Y7617FC447":72},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":26,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":49,"commitSha":50,"skillDirectory":51,"forksCount":52,"repositoryHtmlUrl":53,"repositoryHomepageUrl":6,"ownerHtmlUrl":54,"ownerBlogUrl":55,"ownerDescription":56,"updatedAt":57,"install":58,"resources":71},"01M6G2F98BVMEDD2Y7617FC447","tdd-workflow",null,"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.","oabdelmaksoud\u002FAgention","oabdelmaksoud","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F50699149?v=4","01M6D5CVVN778ZCDP1WHS816SP","Omar Abdelmaksoud","01M6D8A4TE76V3DCSH3D94VG1V",2,"MIT","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fdocs\u002Fzh-TW\u002Fskills\u002Ftdd-workflow\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"tdd","tdd workflow","workflow","test","e2e tests","development","code","integration","writing","e2e","refactoring","coverage","use","tests","driven","unit","with","2026-11-10 10:12:30",false,"---\nname: tdd-workflow\ndescription: 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.\n---\n\n# 測試驅動開發工作流程\n\n此技能確保所有程式碼開發遵循 TDD 原則，並具有完整的測試覆蓋率。\n\n## 何時啟用\n\n- 撰寫新功能或功能性程式碼\n- 修復 Bug 或問題\n- 重構現有程式碼\n- 新增 API 端點\n- 建立新元件\n\n## 核心原則\n\n### 1. 測試先於程式碼\n總是先寫測試，然後實作程式碼使測試通過。\n\n### 2. 覆蓋率要求\n- 最低 80% 覆蓋率（單元 + 整合 + E2E）\n- 涵蓋所有邊界案例\n- 測試錯誤情境\n- 驗證邊界條件\n\n### 3. 測試類型\n\n#### 單元測試\n- 個別函式和工具\n- 元件邏輯\n- 純函式\n- 輔助函式和工具\n\n#### 整合測試\n- API 端點\n- 資料庫操作\n- 服務互動\n- 外部 API 呼叫\n\n#### E2E 測試（Playwright）\n- 關鍵使用者流程\n- 完整工作流程\n- 瀏覽器自動化\n- UI 互動\n\n## TDD 工作流程步驟\n\n### 步驟 1：撰寫使用者旅程\n```\n身為 [角色]，我想要 [動作]，以便 [好處]\n\n範例：\n身為使用者，我想要語意搜尋市場，\n以便即使沒有精確關鍵字也能找到相關市場。\n```\n\n### 步驟 2：產生測試案例\n為每個使用者旅程建立完整的測試案例：\n\n```typescript\ndescribe('Semantic Search', () => {\n  it('returns relevant markets for query', async () => {\n    \u002F\u002F 測試實作\n  })\n\n  it('handles empty query gracefully', async () => {\n    \u002F\u002F 測試邊界案例\n  })\n\n  it('falls back to substring search when Redis unavailable', async () => {\n    \u002F\u002F 測試回退行為\n  })\n\n  it('sorts results by similarity score', async () => {\n    \u002F\u002F 測試排序邏輯\n  })\n})\n```\n\n### 步驟 3：執行測試（應該失敗）\n```bash\nnpm test\n# 測試應該失敗 - 我們還沒實作\n```\n\n### 步驟 4：實作程式碼\n撰寫最少的程式碼使測試通過：\n\n```typescript\n\u002F\u002F 由測試引導的實作\nexport async function searchMarkets(query: string) {\n  \u002F\u002F 實作在此\n}\n```\n\n### 步驟 5：再次執行測試\n```bash\nnpm test\n# 測試現在應該通過\n```\n\n### 步驟 6：重構\n在保持測試通過的同時改善程式碼品質：\n- 移除重複\n- 改善命名\n- 優化效能\n- 增強可讀性\n\n### 步驟 7：驗證覆蓋率\n```bash\nnpm run test:coverage\n# 驗證達到 80%+ 覆蓋率\n```\n\n## 測試模式\n\n### 單元測試模式（Jest\u002FVitest）\n```typescript\nimport { render, screen, fireEvent } from '@testing-library\u002Freact'\nimport { Button } from '.\u002FButton'\n\ndescribe('Button Component', () => {\n  it('renders with correct text', () => {\n    render(\u003CButton>Click me\u003C\u002FButton>)\n    expect(screen.getByText('Click me')).toBeInTheDocument()\n  })\n\n  it('calls onClick when clicked', () => {\n    const handleClick = jest.fn()\n    render(\u003CButton onClick={handleClick}>Click\u003C\u002FButton>)\n\n    fireEvent.click(screen.getByRole('button'))\n\n    expect(handleClick).toHaveBeenCalledTimes(1)\n  })\n\n  it('is disabled when disabled prop is true', () => {\n    render(\u003CButton disabled>Click\u003C\u002FButton>)\n    expect(screen.getByRole('button')).toBeDisabled()\n  })\n})\n```\n\n### API 整合測試模式\n```typescript\nimport { NextRequest } from 'next\u002Fserver'\nimport { GET } from '.\u002Froute'\n\ndescribe('GET \u002Fapi\u002Fmarkets', () => {\n  it('returns markets successfully', async () => {\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets')\n    const response = await GET(request)\n    const data = await response.json()\n\n    expect(response.status).toBe(200)\n    expect(data.success).toBe(true)\n    expect(Array.isArray(data.data)).toBe(true)\n  })\n\n  it('validates query parameters', async () => {\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets?limit=invalid')\n    const response = await GET(request)\n\n    expect(response.status).toBe(400)\n  })\n\n  it('handles database errors gracefully', async () => {\n    \u002F\u002F Mock 資料庫失敗\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets')\n    \u002F\u002F 測試錯誤處理\n  })\n})\n```\n\n### E2E 測試模式（Playwright）\n```typescript\nimport { test, expect } from '@playwright\u002Ftest'\n\ntest('user can search and filter markets', async ({ page }) => {\n  \u002F\u002F 導航到市場頁面\n  await page.goto('\u002F')\n  await page.click('a[href=\"\u002Fmarkets\"]')\n\n  \u002F\u002F 驗證頁面載入\n  await expect(page.locator('h1')).toContainText('Markets')\n\n  \u002F\u002F 搜尋市場\n  await page.fill('input[placeholder=\"Search markets\"]', 'election')\n\n  \u002F\u002F 等待 debounce 和結果\n  await page.waitForTimeout(600)\n\n  \u002F\u002F 驗證搜尋結果顯示\n  const results = page.locator('[data-testid=\"market-card\"]')\n  await expect(results).toHaveCount(5, { timeout: 5000 })\n\n  \u002F\u002F 驗證結果包含搜尋詞\n  const firstResult = results.first()\n  await expect(firstResult).toContainText('election', { ignoreCase: true })\n\n  \u002F\u002F 依狀態篩選\n  await page.click('button:has-text(\"Active\")')\n\n  \u002F\u002F 驗證篩選結果\n  await expect(results).toHaveCount(3)\n})\n\ntest('user can create a new market', async ({ page }) => {\n  \u002F\u002F 先登入\n  await page.goto('\u002Fcreator-dashboard')\n\n  \u002F\u002F 填寫市場建立表單\n  await page.fill('input[name=\"name\"]', 'Test Market')\n  await page.fill('textarea[name=\"description\"]', 'Test description')\n  await page.fill('input[name=\"endDate\"]', '2025-12-31')\n\n  \u002F\u002F 提交表單\n  await page.click('button[type=\"submit\"]')\n\n  \u002F\u002F 驗證成功訊息\n  await expect(page.locator('text=Market created successfully')).toBeVisible()\n\n  \u002F\u002F 驗證重導向到市場頁面\n  await expect(page).toHaveURL(\u002F\\\u002Fmarkets\\\u002Ftest-market\u002F)\n})\n```\n\n## 測試檔案組織\n\n```\nsrc\u002F\n├── components\u002F\n│   ├── Button\u002F\n│   │   ├── Button.tsx\n│   │   ├── Button.test.tsx          # 單元測試\n│   │   └── Button.stories.tsx       # Storybook\n│   └── MarketCard\u002F\n│       ├── MarketCard.tsx\n│       └── MarketCard.test.tsx\n├── app\u002F\n│   └── api\u002F\n│       └── markets\u002F\n│           ├── route.ts\n│           └── route.test.ts         # 整合測試\n└── e2e\u002F\n    ├── markets.spec.ts               # E2E 測試\n    ├── trading.spec.ts\n    └── auth.spec.ts\n```\n\n## Mock 外部服務\n\n### Supabase Mock\n```typescript\njest.mock('@\u002Flib\u002Fsupabase', () => ({\n  supabase: {\n    from: jest.fn(() => ({\n      select: jest.fn(() => ({\n        eq: jest.fn(() => Promise.resolve({\n          data: [{ id: 1, name: 'Test Market' }],\n          error: null\n        }))\n      }))\n    }))\n  }\n}))\n```\n\n### Redis Mock\n```typescript\njest.mock('@\u002Flib\u002Fredis', () => ({\n  searchMarketsByVector: jest.fn(() => Promise.resolve([\n    { slug: 'test-market', similarity_score: 0.95 }\n  ])),\n  checkRedisHealth: jest.fn(() => Promise.resolve({ connected: true }))\n}))\n```\n\n### OpenAI Mock\n```typescript\njest.mock('@\u002Flib\u002Fopenai', () => ({\n  generateEmbedding: jest.fn(() => Promise.resolve(\n    new Array(1536).fill(0.1) \u002F\u002F Mock 1536 維嵌入向量\n  ))\n}))\n```\n\n## 測試覆蓋率驗證\n\n### 執行覆蓋率報告\n```bash\nnpm run test:coverage\n```\n\n### 覆蓋率門檻\n```json\n{\n  \"jest\": {\n    \"coverageThresholds\": {\n      \"global\": {\n        \"branches\": 80,\n        \"functions\": 80,\n        \"lines\": 80,\n        \"statements\": 80\n      }\n    }\n  }\n}\n```\n\n## 常見測試錯誤避免\n\n### ❌ 錯誤：測試實作細節\n```typescript\n\u002F\u002F 不要測試內部狀態\nexpect(component.state.count).toBe(5)\n```\n\n### ✅ 正確：測試使用者可見行為\n```typescript\n\u002F\u002F 測試使用者看到的內容\nexpect(screen.getByText('Count: 5')).toBeInTheDocument()\n```\n\n### ❌ 錯誤：脆弱的選擇器\n```typescript\n\u002F\u002F 容易壞掉\nawait page.click('.css-class-xyz')\n```\n\n### ✅ 正確：語意選擇器\n```typescript\n\u002F\u002F 對變更有彈性\nawait page.click('button:has-text(\"Submit\")')\nawait page.click('[data-testid=\"submit-button\"]')\n```\n\n### ❌ 錯誤：無測試隔離\n```typescript\n\u002F\u002F 測試互相依賴\ntest('creates user', () => { \u002F* ... *\u002F })\ntest('updates same user', () => { \u002F* 依賴前一個測試 *\u002F })\n```\n\n### ✅ 正確：獨立測試\n```typescript\n\u002F\u002F 每個測試設置自己的資料\ntest('creates user', () => {\n  const user = createTestUser()\n  \u002F\u002F 測試邏輯\n})\n\ntest('updates user', () => {\n  const user = createTestUser()\n  \u002F\u002F 更新邏輯\n})\n```\n\n## 持續測試\n\n### 開發期間的 Watch 模式\n```bash\nnpm test -- --watch\n# 檔案變更時自動執行測試\n```\n\n### Pre-Commit Hook\n```bash\n# 每次 commit 前執行\nnpm test && npm run lint\n```\n\n### CI\u002FCD 整合\n```yaml\n# GitHub Actions\n- name: Run Tests\n  run: npm test -- --coverage\n- name: Upload Coverage\n  uses: codecov\u002Fcodecov-action@v3\n```\n\n## 最佳實務\n\n1. **先寫測試** - 總是 TDD\n2. **一個測試一個斷言** - 專注單一行為\n3. **描述性測試名稱** - 解釋測試內容\n4. **Arrange-Act-Assert** - 清晰的測試結構\n5. **Mock 外部依賴** - 隔離單元測試\n6. **測試邊界案例** - Null、undefined、空值、大值\n7. **測試錯誤路徑** - 不只是快樂路徑\n8. **保持測試快速** - 單元測試每個 \u003C 50ms\n9. **測試後清理** - 無副作用\n10. **檢視覆蓋率報告** - 識別缺口\n\n## 成功指標\n\n- 達到 80%+ 程式碼覆蓋率\n- 所有測試通過（綠色）\n- 無跳過或停用的測試\n- 快速測試執行（單元測試 \u003C 30s）\n- E2E 測試涵蓋關鍵使用者流程\n- 測試在生產前捕捉 Bug\n\n---\n\n**記住**：測試不是可選的。它們是實現自信重構、快速開發和生產可靠性的安全網。\n","# 測試驅動開發工作流程\n\n此技能確保所有程式碼開發遵循 TDD 原則，並具有完整的測試覆蓋率。\n\n## 何時啟用\n\n- 撰寫新功能或功能性程式碼\n- 修復 Bug 或問題\n- 重構現有程式碼\n- 新增 API 端點\n- 建立新元件\n\n## 核心原則\n\n### 1. 測試先於程式碼\n總是先寫測試，然後實作程式碼使測試通過。\n\n### 2. 覆蓋率要求\n- 最低 80% 覆蓋率（單元 + 整合 + E2E）\n- 涵蓋所有邊界案例\n- 測試錯誤情境\n- 驗證邊界條件\n\n### 3. 測試類型\n\n#### 單元測試\n- 個別函式和工具\n- 元件邏輯\n- 純函式\n- 輔助函式和工具\n\n#### 整合測試\n- API 端點\n- 資料庫操作\n- 服務互動\n- 外部 API 呼叫\n\n#### E2E 測試（Playwright）\n- 關鍵使用者流程\n- 完整工作流程\n- 瀏覽器自動化\n- UI 互動\n\n## TDD 工作流程步驟\n\n### 步驟 1：撰寫使用者旅程\n```\n身為 [角色]，我想要 [動作]，以便 [好處]\n\n範例：\n身為使用者，我想要語意搜尋市場，\n以便即使沒有精確關鍵字也能找到相關市場。\n```\n\n### 步驟 2：產生測試案例\n為每個使用者旅程建立完整的測試案例：\n\n```typescript\ndescribe('Semantic Search', () => {\n  it('returns relevant markets for query', async () => {\n    \u002F\u002F 測試實作\n  })\n\n  it('handles empty query gracefully', async () => {\n    \u002F\u002F 測試邊界案例\n  })\n\n  it('falls back to substring search when Redis unavailable', async () => {\n    \u002F\u002F 測試回退行為\n  })\n\n  it('sorts results by similarity score', async () => {\n    \u002F\u002F 測試排序邏輯\n  })\n})\n```\n\n### 步驟 3：執行測試（應該失敗）\n```bash\nnpm test\n# 測試應該失敗 - 我們還沒實作\n```\n\n### 步驟 4：實作程式碼\n撰寫最少的程式碼使測試通過：\n\n```typescript\n\u002F\u002F 由測試引導的實作\nexport async function searchMarkets(query: string) {\n  \u002F\u002F 實作在此\n}\n```\n\n### 步驟 5：再次執行測試\n```bash\nnpm test\n# 測試現在應該通過\n```\n\n### 步驟 6：重構\n在保持測試通過的同時改善程式碼品質：\n- 移除重複\n- 改善命名\n- 優化效能\n- 增強可讀性\n\n### 步驟 7：驗證覆蓋率\n```bash\nnpm run test:coverage\n# 驗證達到 80%+ 覆蓋率\n```\n\n## 測試模式\n\n### 單元測試模式（Jest\u002FVitest）\n```typescript\nimport { render, screen, fireEvent } from '@testing-library\u002Freact'\nimport { Button } from '.\u002FButton'\n\ndescribe('Button Component', () => {\n  it('renders with correct text', () => {\n    render(\u003CButton>Click me\u003C\u002FButton>)\n    expect(screen.getByText('Click me')).toBeInTheDocument()\n  })\n\n  it('calls onClick when clicked', () => {\n    const handleClick = jest.fn()\n    render(\u003CButton onClick={handleClick}>Click\u003C\u002FButton>)\n\n    fireEvent.click(screen.getByRole('button'))\n\n    expect(handleClick).toHaveBeenCalledTimes(1)\n  })\n\n  it('is disabled when disabled prop is true', () => {\n    render(\u003CButton disabled>Click\u003C\u002FButton>)\n    expect(screen.getByRole('button')).toBeDisabled()\n  })\n})\n```\n\n### API 整合測試模式\n```typescript\nimport { NextRequest } from 'next\u002Fserver'\nimport { GET } from '.\u002Froute'\n\ndescribe('GET \u002Fapi\u002Fmarkets', () => {\n  it('returns markets successfully', async () => {\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets')\n    const response = await GET(request)\n    const data = await response.json()\n\n    expect(response.status).toBe(200)\n    expect(data.success).toBe(true)\n    expect(Array.isArray(data.data)).toBe(true)\n  })\n\n  it('validates query parameters', async () => {\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets?limit=invalid')\n    const response = await GET(request)\n\n    expect(response.status).toBe(400)\n  })\n\n  it('handles database errors gracefully', async () => {\n    \u002F\u002F Mock 資料庫失敗\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets')\n    \u002F\u002F 測試錯誤處理\n  })\n})\n```\n\n### E2E 測試模式（Playwright）\n```typescript\nimport { test, expect } from '@playwright\u002Ftest'\n\ntest('user can search and filter markets', async ({ page }) => {\n  \u002F\u002F 導航到市場頁面\n  await page.goto('\u002F')\n  await page.click('a[href=\"\u002Fmarkets\"]')\n\n  \u002F\u002F 驗證頁面載入\n  await expect(page.locator('h1')).toContainText('Markets')\n\n  \u002F\u002F 搜尋市場\n  await page.fill('input[placeholder=\"Search markets\"]', 'election')\n\n  \u002F\u002F 等待 debounce 和結果\n  await page.waitForTimeout(600)\n\n  \u002F\u002F 驗證搜尋結果顯示\n  const results = page.locator('[data-testid=\"market-card\"]')\n  await expect(results).toHaveCount(5, { timeout: 5000 })\n\n  \u002F\u002F 驗證結果包含搜尋詞\n  const firstResult = results.first()\n  await expect(firstResult).toContainText('election', { ignoreCase: true })\n\n  \u002F\u002F 依狀態篩選\n  await page.click('button:has-text(\"Active\")')\n\n  \u002F\u002F 驗證篩選結果\n  await expect(results).toHaveCount(3)\n})\n\ntest('user can create a new market', async ({ page }) => {\n  \u002F\u002F 先登入\n  await page.goto('\u002Fcreator-dashboard')\n\n  \u002F\u002F 填寫市場建立表單\n  await page.fill('input[name=\"name\"]', 'Test Market')\n  await page.fill('textarea[name=\"description\"]', 'Test description')\n  await page.fill('input[name=\"endDate\"]', '2025-12-31')\n\n  \u002F\u002F 提交表單\n  await page.click('button[type=\"submit\"]')\n\n  \u002F\u002F 驗證成功訊息\n  await expect(page.locator('text=Market created successfully')).toBeVisible()\n\n  \u002F\u002F 驗證重導向到市場頁面\n  await expect(page).toHaveURL(\u002F\\\u002Fmarkets\\\u002Ftest-market\u002F)\n})\n```\n\n## 測試檔案組織\n\n```\nsrc\u002F\n├── components\u002F\n│   ├── Button\u002F\n│   │   ├── Button.tsx\n│   │   ├── Button.test.tsx          # 單元測試\n│   │   └── Button.stories.tsx       # Storybook\n│   └── MarketCard\u002F\n│       ├── MarketCard.tsx\n│       └── MarketCard.test.tsx\n├── app\u002F\n│   └── api\u002F\n│       └── markets\u002F\n│           ├── route.ts\n│           └── route.test.ts         # 整合測試\n└── e2e\u002F\n    ├── markets.spec.ts               # E2E 測試\n    ├── trading.spec.ts\n    └── auth.spec.ts\n```\n\n## Mock 外部服務\n\n### Supabase Mock\n```typescript\njest.mock('@\u002Flib\u002Fsupabase', () => ({\n  supabase: {\n    from: jest.fn(() => ({\n      select: jest.fn(() => ({\n        eq: jest.fn(() => Promise.resolve({\n          data: [{ id: 1, name: 'Test Market' }],\n          error: null\n        }))\n      }))\n    }))\n  }\n}))\n```\n\n### Redis Mock\n```typescript\njest.mock('@\u002Flib\u002Fredis', () => ({\n  searchMarketsByVector: jest.fn(() => Promise.resolve([\n    { slug: 'test-market', similarity_score: 0.95 }\n  ])),\n  checkRedisHealth: jest.fn(() => Promise.resolve({ connected: true }))\n}))\n```\n\n### OpenAI Mock\n```typescript\njest.mock('@\u002Flib\u002Fopenai', () => ({\n  generateEmbedding: jest.fn(() => Promise.resolve(\n    new Array(1536).fill(0.1) \u002F\u002F Mock 1536 維嵌入向量\n  ))\n}))\n```\n\n## 測試覆蓋率驗證\n\n### 執行覆蓋率報告\n```bash\nnpm run test:coverage\n```\n\n### 覆蓋率門檻\n```json\n{\n  \"jest\": {\n    \"coverageThresholds\": {\n      \"global\": {\n        \"branches\": 80,\n        \"functions\": 80,\n        \"lines\": 80,\n        \"statements\": 80\n      }\n    }\n  }\n}\n```\n\n## 常見測試錯誤避免\n\n### ❌ 錯誤：測試實作細節\n```typescript\n\u002F\u002F 不要測試內部狀態\nexpect(component.state.count).toBe(5)\n```\n\n### ✅ 正確：測試使用者可見行為\n```typescript\n\u002F\u002F 測試使用者看到的內容\nexpect(screen.getByText('Count: 5')).toBeInTheDocument()\n```\n\n### ❌ 錯誤：脆弱的選擇器\n```typescript\n\u002F\u002F 容易壞掉\nawait page.click('.css-class-xyz')\n```\n\n### ✅ 正確：語意選擇器\n```typescript\n\u002F\u002F 對變更有彈性\nawait page.click('button:has-text(\"Submit\")')\nawait page.click('[data-testid=\"submit-button\"]')\n```\n\n### ❌ 錯誤：無測試隔離\n```typescript\n\u002F\u002F 測試互相依賴\ntest('creates user', () => { \u002F* ... *\u002F })\ntest('updates same user', () => { \u002F* 依賴前一個測試 *\u002F })\n```\n\n### ✅ 正確：獨立測試\n```typescript\n\u002F\u002F 每個測試設置自己的資料\ntest('creates user', () => {\n  const user = createTestUser()\n  \u002F\u002F 測試邏輯\n})\n\ntest('updates user', () => {\n  const user = createTestUser()\n  \u002F\u002F 更新邏輯\n})\n```\n\n## 持續測試\n\n### 開發期間的 Watch 模式\n```bash\nnpm test -- --watch\n# 檔案變更時自動執行測試\n```\n\n### Pre-Commit Hook\n```bash\n# 每次 commit 前執行\nnpm test && npm run lint\n```\n\n### CI\u002FCD 整合\n```yaml\n# GitHub Actions\n- name: Run Tests\n  run: npm test -- --coverage\n- name: Upload Coverage\n  uses: codecov\u002Fcodecov-action@v3\n```\n\n## 最佳實務\n\n1. **先寫測試** - 總是 TDD\n2. **一個測試一個斷言** - 專注單一行為\n3. **描述性測試名稱** - 解釋測試內容\n4. **Arrange-Act-Assert** - 清晰的測試結構\n5. **Mock 外部依賴** - 隔離單元測試\n6. **測試邊界案例** - Null、undefined、空值、大值\n7. **測試錯誤路徑** - 不只是快樂路徑\n8. **保持測試快速** - 單元測試每個 \u003C 50ms\n9. **測試後清理** - 無副作用\n10. **檢視覆蓋率報告** - 識別缺口\n\n## 成功指標\n\n- 達到 80%+ 程式碼覆蓋率\n- 所有測試通過（綠色）\n- 無跳過或停用的測試\n- 快速測試執行（單元測試 \u003C 30s）\n- E2E 測試涵蓋關鍵使用者流程\n- 測試在生產前捕捉 Bug\n\n---\n\n**記住**：測試不是可選的。它們是實現自信重構、快速開發和生產可靠性的安全網。\n",{"name":5,"description":7},"main","a318f48de3c5718e8ea2e9f6fae7790e8a759e85","upstream-sync\u002Fwork\u002Fecc\u002Fdocs\u002Fzh-TW\u002Fskills\u002Ftdd-workflow",0,"https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fo-abdelmaksoud\u002F","Full-Stack Engineer | AI Systems Architect | Building autonomous multi-agent systems","2026-03-08 21:23:03",{"source":8,"skillName":5,"commands":59,"prompt":69,"zipAvailable":70},[60,63,66],{"tool":61,"command":62},"npx","npx skills add oabdelmaksoud\u002FAgention --skill tdd-workflow",{"tool":64,"command":65},"bunx","bunx skills add oabdelmaksoud\u002FAgention --skill tdd-workflow",{"tool":67,"command":68},"pnpm","pnpm dlx skills add oabdelmaksoud\u002FAgention --skill tdd-workflow","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fdocs\u002Fzh-TW\u002Fskills\u002Ftdd-workflow\u002FSKILL.md",true,[],[73,94,107,119,145,165],{"skillNo":74,"name":75,"nameZh":6,"description":76,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":77,"categories":78,"keywords":82,"publishedAt":44},"01M6G2QFCBNBYAJF2KTK85G9A6","visa-doc-translate","Translate visa application documents (images) to English and create a bilingual PDF with original and translation","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fvisa-doc-translate\u002FSKILL.md",[79],{"categoryNo":80,"categoryName":81,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000002","文档处理",[83,84,85,86,43,87,88,89,90,91,92,93],"translate","doc","visa doc translate","bilingual","english","application","PDF","translation","documents","create","images",{"skillNo":95,"name":96,"nameZh":6,"description":97,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":98,"categories":99,"keywords":100,"publishedAt":44},"01M6G2QB09TSK6BY4ME5X20EVQ","verification-loop","A comprehensive verification system for Claude Code sessions.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fverification-loop\u002FSKILL.md",[],[101,102,103,104,33,105,106],"verification","loop","verification loop","sessions","claude code sessions","system",{"skillNo":108,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":109,"categories":110,"keywords":118,"publishedAt":44},"01M6G2Q6V2F7G03VAAF27FXPJR","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Ftdd-workflow\u002FSKILL.md",[111,112,113],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":114,"categoryName":115,"taxonomyType":116,"taxonomyTypeDesc":117},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[29,27,28,39,42,38,37,36,34,33,32,31,40,41,43,30,35],{"skillNo":120,"name":121,"nameZh":6,"description":122,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":123,"categories":124,"keywords":125,"publishedAt":44},"01M6G2Q359YJRWAR2PFCEQAXF3","swiftui-patterns","SwiftUI architecture patterns, state management with @Observable, view composition, navigation, performance optimization, and modern iOS\u002FmacOS UI best practices.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fswiftui-patterns\u002FSKILL.md",[],[126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,43],"patterns","swiftui","swiftui patterns","architecture","macos","performance","state management","best practices","state","ui","Optimization","ios","architecture patterns","Composition","performance optimization","view","management","swiftui architecture","navigation",{"skillNo":146,"name":147,"nameZh":6,"description":148,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":149,"categories":150,"keywords":152,"publishedAt":44},"01M6G2PY8B2FWZTWPZ84H83Y7C","swift-protocol-di-testing","Protocol-based dependency injection for testable Swift code — mock file system, network, and external APIs using focused protocols and Swift Testing.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fswift-protocol-di-testing\u002FSKILL.md",[151],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[153,154,155,156,33,157,158,159,160,106,161,162,163,164],"swift protocol di testing","protocol","swift","testing","protocols","file","network","injection","dependency","swift testing","dependency injection","mock",{"skillNo":166,"name":167,"nameZh":6,"description":168,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":169,"categories":170,"keywords":171,"publishedAt":44},"01M6G2PSCN2GXV9FFZDRRRWVB6","swift-concurrency-6-2","Swift 6.2 Approachable Concurrency — single-threaded by default, @concurrent for explicit background offloading, isolated conformances for main actor types.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fswift-concurrency-6-2\u002FSKILL.md",[],[172,173,174,155,175,49,176],"concurrency","swift concurrency","swift concurrency 6 2","default","types"]