Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    zhukunpenglinyutong

    tdd-workflow

    zhukunpenglinyutong/tdd-workflow
    Coding
    109
    1 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    在编写新功能、修复 bug 或重构代码时使用此 skill。强制执行测试驱动开发,覆盖率要求 80% 以上,包括单元测试、集成测试和 E2E 测试。

    SKILL.md

    测试驱动开发工作流

    此 skill 确保所有代码开发遵循 TDD 原则并具有全面的测试覆盖。

    何时激活

    • 编写新功能或特性
    • 修复 bug 或问题
    • 重构现有代码
    • 添加 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:运行测试(它们应该失败)

    npm test
    # 测试应该失败 - 我们还没有实现
    

    步骤 4:实现代码

    编写最少的代码使测试通过:

    // 由测试指导的实现
    export async function searchMarkets(query: string) {
      // 这里是实现
    }
    

    步骤 5:再次运行测试

    npm test
    # 测试现在应该通过
    

    步骤 6:重构

    在保持测试绿色的情况下改进代码质量:

    • 消除重复
    • 改进命名
    • 优化性能
    • 增强可读性

    步骤 7:验证覆盖率

    npm run test:coverage
    # 验证达到 80%+ 覆盖率
    

    测试模式

    单元测试模式(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) // 模拟 1536 维 embedding
      ))
    }))
    

    测试覆盖率验证

    运行覆盖率报告

    npm run test:coverage
    

    覆盖率阈值

    {
      "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()
      // 更新逻辑
    })
    

    持续测试

    开发时的监视模式

    npm test -- --watch
    # 文件更改时自动运行测试
    

    预提交钩子

    # 每次提交前运行
    npm test && npm run lint
    

    CI/CD 集成

    # GitHub Actions
    - name: Run Tests
      run: npm test -- --coverage
    - name: Upload Coverage
      uses: codecov/codecov-action@v3
    

    最佳实践

    1. 先写测试 - 始终 TDD
    2. 每个测试一个断言 - 聚焦单一行为
    3. 描述性测试名称 - 解释测试什么
    4. 准备-执行-断言 - 清晰的测试结构
    5. 模拟外部依赖 - 隔离单元测试
    6. 测试边界情况 - Null、undefined、空、大值
    7. 测试错误路径 - 不只是快乐路径
    8. 保持测试快速 - 单元测试每个 < 50ms
    9. 测试后清理 - 无副作用
    10. 查看覆盖率报告 - 识别差距

    成功指标

    • 达到 80%+ 代码覆盖率
    • 所有测试通过(绿色)
    • 没有跳过或禁用的测试
    • 快速测试执行(单元测试 < 30秒)
    • E2E 测试覆盖关键用户流程
    • 测试在生产前捕获 bug

    切记:测试不是可选的。它们是支撑自信重构、快速开发和生产可靠性的安全网。

    Repository
    zhukunpenglinyutong/ai-max
    Files