[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6G2D48240QCKMDK0B0MBD3R":3,"skill-same:01M6G2D48240QCKMDK0B0MBD3R":62},{"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":28,"publishedAt":33,"linkOnly":34,"markdownContent":35,"markdownBody":36,"frontMatter":37,"refName":39,"commitSha":40,"skillDirectory":41,"forksCount":42,"repositoryHtmlUrl":43,"repositoryHomepageUrl":6,"ownerHtmlUrl":44,"ownerBlogUrl":45,"ownerDescription":46,"updatedAt":47,"install":48,"resources":61},"01M6G2D48240QCKMDK0B0MBD3R","tdd-workflow",null,"在编写新功能、修复错误或重构代码时使用此技能。强制执行测试驱动开发，确保单元测试、集成测试和端到端测试的覆盖率超过80%。","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-CN\u002Fskills\u002Ftdd-workflow\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[29,30,31,32],"tdd","workflow","tdd workflow","testing","2026-11-10 10:12:30",false,"---\nname: tdd-workflow\ndescription: 在编写新功能、修复错误或重构代码时使用此技能。强制执行测试驱动开发，确保单元测试、集成测试和端到端测试的覆盖率超过80%。\norigin: ECC\n---\n\n# 测试驱动开发工作流\n\n此技能确保所有代码开发遵循TDD原则，并具备全面的测试覆盖率。\n\n## 何时激活\n\n* 编写新功能或功能\n* 修复错误或问题\n* 重构现有代码\n* 添加API端点\n* 创建新组件\n\n## 核心原则\n\n### 1. 测试优先于代码\n\n始终先编写测试，然后实现代码以使测试通过。\n\n### 2. 覆盖率要求\n\n* 最低80%覆盖率（单元 + 集成 + 端到端）\n* 覆盖所有边缘情况\n* 测试错误场景\n* 验证边界条件\n\n### 3. 测试类型\n\n#### 单元测试\n\n* 单个函数和工具\n* 组件逻辑\n* 纯函数\n* 辅助函数和工具\n\n#### 集成测试\n\n* API端点\n* 数据库操作\n* 服务交互\n* 外部API调用\n\n#### 端到端测试 (Playwright)\n\n* 关键用户流程\n* 完整工作流\n* 浏览器自动化\n* UI交互\n\n## TDD 工作流步骤\n\n### 步骤 1: 编写用户旅程\n\n```\nAs a [role], I want to [action], so that [benefit]\n\nExample:\nAs a user, I want to search for markets semantically,\nso that I can find relevant markets even without exact keywords.\n```\n\n### 步骤 2: 生成测试用例\n\n针对每个用户旅程，创建全面的测试用例：\n\n```typescript\ndescribe('Semantic Search', () => {\n  it('returns relevant markets for query', async () => {\n    \u002F\u002F Test implementation\n  })\n\n  it('handles empty query gracefully', async () => {\n    \u002F\u002F Test edge case\n  })\n\n  it('falls back to substring search when Redis unavailable', async () => {\n    \u002F\u002F Test fallback behavior\n  })\n\n  it('sorts results by similarity score', async () => {\n    \u002F\u002F Test sorting logic\n  })\n})\n```\n\n### 步骤 3: 运行测试（它们应该失败）\n\n```bash\nnpm test\n# Tests should fail - we haven't implemented yet\n```\n\n### 步骤 4: 实现代码\n\n编写最少的代码以使测试通过：\n\n```typescript\n\u002F\u002F Implementation guided by tests\nexport async function searchMarkets(query: string) {\n  \u002F\u002F Implementation here\n}\n```\n\n### 步骤 5: 再次运行测试\n\n```bash\nnpm test\n# Tests should now pass\n```\n\n### 步骤 6: 重构\n\n在保持测试通过的同时提高代码质量：\n\n* 消除重复\n* 改进命名\n* 优化性能\n* 增强可读性\n\n### 步骤 7: 验证覆盖率\n\n```bash\nnpm run test:coverage\n# Verify 80%+ coverage achieved\n```\n\n## 测试模式\n\n### 单元测试模式 (Jest\u002FVitest)\n\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\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 database failure\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets')\n    \u002F\u002F Test error handling\n  })\n})\n```\n\n### 端到端测试模式 (Playwright)\n\n```typescript\nimport { test, expect } from '@playwright\u002Ftest'\n\ntest('user can search and filter markets', async ({ page }) => {\n  \u002F\u002F Navigate to markets page\n  await page.goto('\u002F')\n  await page.click('a[href=\"\u002Fmarkets\"]')\n\n  \u002F\u002F Verify page loaded\n  await expect(page.locator('h1')).toContainText('Markets')\n\n  \u002F\u002F Search for markets\n  await page.fill('input[placeholder=\"Search markets\"]', 'election')\n\n  \u002F\u002F Wait for debounce and results\n  await page.waitForTimeout(600)\n\n  \u002F\u002F Verify search results displayed\n  const results = page.locator('[data-testid=\"market-card\"]')\n  await expect(results).toHaveCount(5, { timeout: 5000 })\n\n  \u002F\u002F Verify results contain search term\n  const firstResult = results.first()\n  await expect(firstResult).toContainText('election', { ignoreCase: true })\n\n  \u002F\u002F Filter by status\n  await page.click('button:has-text(\"Active\")')\n\n  \u002F\u002F Verify filtered results\n  await expect(results).toHaveCount(3)\n})\n\ntest('user can create a new market', async ({ page }) => {\n  \u002F\u002F Login first\n  await page.goto('\u002Fcreator-dashboard')\n\n  \u002F\u002F Fill market creation form\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 Submit form\n  await page.click('button[type=\"submit\"]')\n\n  \u002F\u002F Verify success message\n  await expect(page.locator('text=Market created successfully')).toBeVisible()\n\n  \u002F\u002F Verify redirect to market page\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          # Unit tests\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         # Integration tests\n└── e2e\u002F\n    ├── markets.spec.ts               # E2E tests\n    ├── trading.spec.ts\n    └── auth.spec.ts\n```\n\n## 模拟外部服务\n\n### Supabase 模拟\n\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 模拟\n\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 模拟\n\n```typescript\njest.mock('@\u002Flib\u002Fopenai', () => ({\n  generateEmbedding: jest.fn(() => Promise.resolve(\n    new Array(1536).fill(0.1) \u002F\u002F Mock 1536-dim embedding\n  ))\n}))\n```\n\n## 测试覆盖率验证\n\n### 运行覆盖率报告\n\n```bash\nnpm run test:coverage\n```\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\n```typescript\n\u002F\u002F Don't test internal state\nexpect(component.state.count).toBe(5)\n```\n\n### ✅ 正确：测试用户可见的行为\n\n```typescript\n\u002F\u002F Test what users see\nexpect(screen.getByText('Count: 5')).toBeInTheDocument()\n```\n\n### ❌ 错误：脆弱的定位器\n\n```typescript\n\u002F\u002F Breaks easily\nawait page.click('.css-class-xyz')\n```\n\n### ✅ 正确：语义化定位器\n\n```typescript\n\u002F\u002F Resilient to changes\nawait page.click('button:has-text(\"Submit\")')\nawait page.click('[data-testid=\"submit-button\"]')\n```\n\n### ❌ 错误：没有测试隔离\n\n```typescript\n\u002F\u002F Tests depend on each other\ntest('creates user', () => { \u002F* ... *\u002F })\ntest('updates same user', () => { \u002F* depends on previous test *\u002F })\n```\n\n### ✅ 正确：独立的测试\n\n```typescript\n\u002F\u002F Each test sets up its own data\ntest('creates user', () => {\n  const user = createTestUser()\n  \u002F\u002F Test logic\n})\n\ntest('updates user', () => {\n  const user = createTestUser()\n  \u002F\u002F Update logic\n})\n```\n\n## 持续测试\n\n### 开发期间的监视模式\n\n```bash\nnpm test -- --watch\n# Tests run automatically on file changes\n```\n\n### 预提交钩子\n\n```bash\n# Runs before every commit\nnpm test && npm run lint\n```\n\n### CI\u002FCD 集成\n\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. **组织-执行-断言** - 清晰的测试结构\n5. **模拟外部依赖** - 隔离单元测试\n6. **测试边缘情况** - Null、undefined、空、大量数据\n7. **测试错误路径** - 不仅仅是正常路径\n8. **保持测试快速** - 单元测试每个 \u003C 50ms\n9. **测试后清理** - 无副作用\n10. **审查覆盖率报告** - 识别空白\n\n## 成功指标\n\n* 达到 80%+ 代码覆盖率\n* 所有测试通过（绿色）\n* 没有跳过或禁用的测试\n* 快速测试执行（单元测试 \u003C 30秒）\n* 端到端测试覆盖关键用户流程\n* 测试在生产前捕获错误\n\n***\n\n**记住**：测试不是可选的。它们是安全网，能够实现自信的重构、快速的开发和生产的可靠性。\n","# 测试驱动开发工作流\n\n此技能确保所有代码开发遵循TDD原则，并具备全面的测试覆盖率。\n\n## 何时激活\n\n* 编写新功能或功能\n* 修复错误或问题\n* 重构现有代码\n* 添加API端点\n* 创建新组件\n\n## 核心原则\n\n### 1. 测试优先于代码\n\n始终先编写测试，然后实现代码以使测试通过。\n\n### 2. 覆盖率要求\n\n* 最低80%覆盖率（单元 + 集成 + 端到端）\n* 覆盖所有边缘情况\n* 测试错误场景\n* 验证边界条件\n\n### 3. 测试类型\n\n#### 单元测试\n\n* 单个函数和工具\n* 组件逻辑\n* 纯函数\n* 辅助函数和工具\n\n#### 集成测试\n\n* API端点\n* 数据库操作\n* 服务交互\n* 外部API调用\n\n#### 端到端测试 (Playwright)\n\n* 关键用户流程\n* 完整工作流\n* 浏览器自动化\n* UI交互\n\n## TDD 工作流步骤\n\n### 步骤 1: 编写用户旅程\n\n```\nAs a [role], I want to [action], so that [benefit]\n\nExample:\nAs a user, I want to search for markets semantically,\nso that I can find relevant markets even without exact keywords.\n```\n\n### 步骤 2: 生成测试用例\n\n针对每个用户旅程，创建全面的测试用例：\n\n```typescript\ndescribe('Semantic Search', () => {\n  it('returns relevant markets for query', async () => {\n    \u002F\u002F Test implementation\n  })\n\n  it('handles empty query gracefully', async () => {\n    \u002F\u002F Test edge case\n  })\n\n  it('falls back to substring search when Redis unavailable', async () => {\n    \u002F\u002F Test fallback behavior\n  })\n\n  it('sorts results by similarity score', async () => {\n    \u002F\u002F Test sorting logic\n  })\n})\n```\n\n### 步骤 3: 运行测试（它们应该失败）\n\n```bash\nnpm test\n# Tests should fail - we haven't implemented yet\n```\n\n### 步骤 4: 实现代码\n\n编写最少的代码以使测试通过：\n\n```typescript\n\u002F\u002F Implementation guided by tests\nexport async function searchMarkets(query: string) {\n  \u002F\u002F Implementation here\n}\n```\n\n### 步骤 5: 再次运行测试\n\n```bash\nnpm test\n# Tests should now pass\n```\n\n### 步骤 6: 重构\n\n在保持测试通过的同时提高代码质量：\n\n* 消除重复\n* 改进命名\n* 优化性能\n* 增强可读性\n\n### 步骤 7: 验证覆盖率\n\n```bash\nnpm run test:coverage\n# Verify 80%+ coverage achieved\n```\n\n## 测试模式\n\n### 单元测试模式 (Jest\u002FVitest)\n\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\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 database failure\n    const request = new NextRequest('http:\u002F\u002Flocalhost\u002Fapi\u002Fmarkets')\n    \u002F\u002F Test error handling\n  })\n})\n```\n\n### 端到端测试模式 (Playwright)\n\n```typescript\nimport { test, expect } from '@playwright\u002Ftest'\n\ntest('user can search and filter markets', async ({ page }) => {\n  \u002F\u002F Navigate to markets page\n  await page.goto('\u002F')\n  await page.click('a[href=\"\u002Fmarkets\"]')\n\n  \u002F\u002F Verify page loaded\n  await expect(page.locator('h1')).toContainText('Markets')\n\n  \u002F\u002F Search for markets\n  await page.fill('input[placeholder=\"Search markets\"]', 'election')\n\n  \u002F\u002F Wait for debounce and results\n  await page.waitForTimeout(600)\n\n  \u002F\u002F Verify search results displayed\n  const results = page.locator('[data-testid=\"market-card\"]')\n  await expect(results).toHaveCount(5, { timeout: 5000 })\n\n  \u002F\u002F Verify results contain search term\n  const firstResult = results.first()\n  await expect(firstResult).toContainText('election', { ignoreCase: true })\n\n  \u002F\u002F Filter by status\n  await page.click('button:has-text(\"Active\")')\n\n  \u002F\u002F Verify filtered results\n  await expect(results).toHaveCount(3)\n})\n\ntest('user can create a new market', async ({ page }) => {\n  \u002F\u002F Login first\n  await page.goto('\u002Fcreator-dashboard')\n\n  \u002F\u002F Fill market creation form\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 Submit form\n  await page.click('button[type=\"submit\"]')\n\n  \u002F\u002F Verify success message\n  await expect(page.locator('text=Market created successfully')).toBeVisible()\n\n  \u002F\u002F Verify redirect to market page\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          # Unit tests\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         # Integration tests\n└── e2e\u002F\n    ├── markets.spec.ts               # E2E tests\n    ├── trading.spec.ts\n    └── auth.spec.ts\n```\n\n## 模拟外部服务\n\n### Supabase 模拟\n\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 模拟\n\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 模拟\n\n```typescript\njest.mock('@\u002Flib\u002Fopenai', () => ({\n  generateEmbedding: jest.fn(() => Promise.resolve(\n    new Array(1536).fill(0.1) \u002F\u002F Mock 1536-dim embedding\n  ))\n}))\n```\n\n## 测试覆盖率验证\n\n### 运行覆盖率报告\n\n```bash\nnpm run test:coverage\n```\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\n```typescript\n\u002F\u002F Don't test internal state\nexpect(component.state.count).toBe(5)\n```\n\n### ✅ 正确：测试用户可见的行为\n\n```typescript\n\u002F\u002F Test what users see\nexpect(screen.getByText('Count: 5')).toBeInTheDocument()\n```\n\n### ❌ 错误：脆弱的定位器\n\n```typescript\n\u002F\u002F Breaks easily\nawait page.click('.css-class-xyz')\n```\n\n### ✅ 正确：语义化定位器\n\n```typescript\n\u002F\u002F Resilient to changes\nawait page.click('button:has-text(\"Submit\")')\nawait page.click('[data-testid=\"submit-button\"]')\n```\n\n### ❌ 错误：没有测试隔离\n\n```typescript\n\u002F\u002F Tests depend on each other\ntest('creates user', () => { \u002F* ... *\u002F })\ntest('updates same user', () => { \u002F* depends on previous test *\u002F })\n```\n\n### ✅ 正确：独立的测试\n\n```typescript\n\u002F\u002F Each test sets up its own data\ntest('creates user', () => {\n  const user = createTestUser()\n  \u002F\u002F Test logic\n})\n\ntest('updates user', () => {\n  const user = createTestUser()\n  \u002F\u002F Update logic\n})\n```\n\n## 持续测试\n\n### 开发期间的监视模式\n\n```bash\nnpm test -- --watch\n# Tests run automatically on file changes\n```\n\n### 预提交钩子\n\n```bash\n# Runs before every commit\nnpm test && npm run lint\n```\n\n### CI\u002FCD 集成\n\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. **组织-执行-断言** - 清晰的测试结构\n5. **模拟外部依赖** - 隔离单元测试\n6. **测试边缘情况** - Null、undefined、空、大量数据\n7. **测试错误路径** - 不仅仅是正常路径\n8. **保持测试快速** - 单元测试每个 \u003C 50ms\n9. **测试后清理** - 无副作用\n10. **审查覆盖率报告** - 识别空白\n\n## 成功指标\n\n* 达到 80%+ 代码覆盖率\n* 所有测试通过（绿色）\n* 没有跳过或禁用的测试\n* 快速测试执行（单元测试 \u003C 30秒）\n* 端到端测试覆盖关键用户流程\n* 测试在生产前捕获错误\n\n***\n\n**记住**：测试不是可选的。它们是安全网，能够实现自信的重构、快速的开发和生产的可靠性。\n",{"name":5,"origin":38,"description":7},"ECC","main","a318f48de3c5718e8ea2e9f6fae7790e8a759e85","upstream-sync\u002Fwork\u002Fecc\u002Fdocs\u002Fzh-CN\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":49,"prompt":59,"zipAvailable":60},[50,53,56],{"tool":51,"command":52},"npx","npx skills add oabdelmaksoud\u002FAgention --skill tdd-workflow",{"tool":54,"command":55},"bunx","bunx skills add oabdelmaksoud\u002FAgention --skill tdd-workflow",{"tool":57,"command":58},"pnpm","pnpm dlx skills add oabdelmaksoud\u002FAgention --skill tdd-workflow","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fdocs\u002Fzh-CN\u002Fskills\u002Ftdd-workflow\u002FSKILL.md",true,[],[63,85,99,122,148,167],{"skillNo":64,"name":65,"nameZh":6,"description":66,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":67,"categories":68,"keywords":72,"publishedAt":33},"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",[69],{"categoryNo":70,"categoryName":71,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000002","文档处理",[73,74,75,76,77,78,79,80,81,82,83,84],"translate","doc","visa doc translate","bilingual","with","english","application","PDF","translation","documents","create","images",{"skillNo":86,"name":87,"nameZh":6,"description":88,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":89,"categories":90,"keywords":91,"publishedAt":33},"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",[],[92,93,94,95,96,97,98],"verification","loop","verification loop","sessions","code","claude code sessions","system",{"skillNo":100,"name":5,"nameZh":6,"description":101,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":102,"categories":103,"keywords":109,"publishedAt":33},"01M6G2Q6V2F7G03VAAF27FXPJR","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.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FAgention\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Ftdd-workflow\u002FSKILL.md",[104,105,108],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":106,"categoryName":107,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},[30,29,31,110,111,112,113,114,115,96,116,117,118,119,77,120,121],"use","unit","coverage","refactoring","e2e","integration","development","e2e tests","tests","driven","test","writing",{"skillNo":123,"name":124,"nameZh":6,"description":125,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":126,"categories":127,"keywords":128,"publishedAt":33},"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",[],[129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,77],"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":149,"name":150,"nameZh":6,"description":151,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":152,"categories":153,"keywords":155,"publishedAt":33},"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",[154],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[156,157,158,32,96,159,160,161,162,98,163,164,165,166],"swift protocol di testing","protocol","swift","protocols","file","network","injection","dependency","swift testing","dependency injection","mock",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":171,"categories":172,"keywords":173,"publishedAt":33},"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",[],[174,175,176,158,177,39,178],"concurrency","swift concurrency","swift concurrency 6 2","default","types"]