mcpField
T
test-automation-expert

test-automation-expert

github.com/curiositech测试与质量257199 次安装2 天前更新
代码仓库

关于 test-automation-expert

为测试策略、自动化测试实现、覆盖率改进和不稳定测试排查提供指导,涵盖单元、集成及端到端测试,并包含 JavaScript/TypeScript 与 Python 示例。

主要功能

  1. 01制定测试策略
  2. 02编写单元、集成和端到端测试
  3. 03选择测试框架
  4. 04提升覆盖率并查找缺口
  5. 05排查不稳定测试
  6. 06配置 CI/CD 测试流水线

适用场景

  1. 01为新项目搭建测试
  2. 02验证关键用户流程
  3. 03诊断不稳定的自动化测试
  4. 04将测试加入 CI/CD

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/curiositech/some_claude_skills --skill test-automation-expert

安装后在 Claude Code 中输入/test-automation-expert确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

CHANGELOG.md
.claude-plugin/
plugin.json
references/
ci-integration.md
coverage-patterns.md
framework-comparison.md
test-strategy.md

详细介绍

测试自动化专家

提供从单元测试到端到端(E2E)的全面测试指导。设计测试策略、实现自动化,并优化覆盖率,以实现可持续的质量保障。

适用场景

适用于:

  • 为新项目设计测试策略
  • 搭建测试框架(Jest、Vitest、Playwright、Cypress、pytest)
  • 编写有效的单元测试、集成测试和 E2E 测试
  • 优化测试覆盖率并消除盲区
  • 调试不稳定的测试
  • 配置 CI/CD 测试流水线
  • 获取测试驱动开发(TDD)指导
  • 制定模拟策略和测试固定装置

不适用于:

  • 编写手动 QA 测试用例——本指南侧重自动化
  • 负载/性能测试——请使用 performance-engineer skill
  • 安全测试——请使用 security-auditor skill
  • 仅进行 API 契约测试——API 设计请使用 backend-architect

测试金字塔理念

         /\
        /  \      E2E Tests (10%)
       /----\     - Critical user journeys
      /      \    - Cross-browser validation
     /--------\
    /          \  Integration Tests (20%)
   /            \ - API contracts
  /--------------\- Component interactions
 /                \
/------------------\ Unit Tests (70%)
                    - Fast, isolated, deterministic
                    - Business logic validation
分布指南
测试类型百分比执行时间目的
单元测试70%< 100ms each逻辑验证
集成测试20%< 1s each组件契约
E2E10%< 30s each关键路径

框架选择

JavaScript/TypeScript
框架最适合速度配置复杂度
VitestVite 项目、现代 ESM最快低
JestReact、成熟项目快中
PlaywrightE2E、跨浏览器N/A低
CypressE2E、组件测试N/A中
Python
框架最适合速度功能
pytest所有场景快固定装置、插件
unittest标准库中内置
hypothesis基于属性的测试不定生成式
决策树:框架选择
New project?
├── Yes → Using Vite?
│   ├── Yes → Vitest
│   └── No → Jest or Vitest (both work)
└── No → What exists?
    ├── Jest → Keep Jest (migration cost rarely worth it)
    ├── Mocha → Consider migration to Vitest
    └── Nothing → Vitest (modern default)

Need E2E?
├── Cross-browser critical → Playwright
├── Developer experience priority → Cypress
└── Both → Playwright (more flexible)

单元测试模式

良好的单元测试结构
describe('UserService', () => {
  describe('validateEmail', () => {
    // Arrange-Act-Assert pattern
    it('should accept valid email formats', () => {
      // Arrange
      const validEmails = ['[email protected]', '[email protected]'];

      // Act & Assert
      validEmails.forEach(email => {
        expect(validateEmail(email)).toBe(true);
      });
    });

    it('should reject invalid email formats', () => {
      // Arrange
      const invalidEmails = ['invalid', '@missing.com', 'no@tld'];

      // Act & Assert
      invalidEmails.forEach(email => {
        expect(validateEmail(email)).toBe(false);
      });
    });

    // Edge cases explicitly tested
    it('should handle empty string', () => {
      expect(validateEmail('')).toBe(false);
    });

    it('should handle null/undefined', () => {
      expect(validateEmail(null)).toBe(false);
      expect(validateEmail(undefined)).toBe(false);
    });
  });
});
模拟策略
// ✅ Good: Mock at boundaries
jest.mock('../services/api', () => ({
  fetchUser: jest.fn()
}));

// ✅ Good: Explicit mock setup per test
beforeEach(() => {
  fetchUser.mockReset();
});

it('handles user not found', async () => {
  fetchUser.mockRejectedValue(new NotFoundError());
  await expect(getUser(123)).rejects.toThrow('User not found');
});

// ❌ Bad: Mocking implementation details
jest.mock('../utils/internal-helper'); // Don't mock internals
测试隔离检查清单
  • 每项测试都能独立运行
  • 测试之间没有共享的可变状态
  • 测试之间重置数据库/API 状态
  • 不依赖测试顺序
  • 可安全并行执行

集成测试模式

API 集成测试
describe('POST /api/users', () => {
  let app;
  let db;

  beforeAll(async () => {
    db = await createTestDatabase();
    app = createApp({ db });
  });

  afterAll(async () => {
    await db.close();
  });

  beforeEach(async () => {
    await db.clear();
  });

  it('creates user with valid data', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({ name: 'Test', email: '[email protected]' })
      .expect(201);

    expect(response.body).toMatchObject({
      id: expect.any(String),
      name: 'Test',
      email: '[email protected]'
    });

    // Verify side effects
    const dbUser = await db.users.findById(response.body.id);
    expect(dbUser).toBeDefined();
  });

  it('rejects duplicate email', async () => {
    await db.users.create({ name: 'Existing', email: '[email protected]' });

    await request(app)
      .post('/api/users')
      .send({ name: 'New', email: '[email protected]' })
      .expect(409);
  });
});
组件集成(React)
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { UserProfile } from './UserProfile';
import { UserProvider } from '../context/UserContext';

describe('UserProfile integration', () => {
  it('loads and displays user data', async () => {
    render(
      <UserProvider>
        <UserProfile userId="123" />
      </UserProvider>
    );

    // Verify loading state
    expect(screen.getByRole('progressbar')).toBeInTheDocument();

    // Wait for data
    await waitFor(() => {
      expect(screen.getByText('John Doe')).toBeInTheDocument();
    });

    // Verify loaded state
    expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
  });
});

E2E 测试模式

Playwright 最佳实践
import { test, expect } from '@playwright/test';

test.describe('Checkout Flow', () => {
  test.beforeEach(async ({ page }) => {
    // Seed test data via API
    await page.request.post('/api/test/seed', {
      data: { scenario: 'checkout-ready' }
    });
  });

  test('complete purchase with credit card', async ({ page }) => {
    await page.goto('/cart');

    // Use accessible selectors
    await page.getByRole('button', { name: 'Proceed to checkout' }).click();

    // Fill payment form
    await page.getByLabel('Card number').fill('4242424242424242');
    await page.getByLabel('Expiry').fill('12/25');
    await page.getByLabel('CVC').fill('123');

    // Complete purchase
    await page.getByRole('button', { name: 'Pay now' }).click();

    // Verify success
    await expect(page.getByRole('heading', { name: 'Order confirmed' })).toBeVisible();
    await expect(page.getByText(/Order #\d+/)).toBeVisible();
  });

  test('shows error for declined card', async ({ page }) => {
    await page.goto('/checkout');

    // Use test card that triggers decline
    await page.getByLabel('Card number').fill('4000000000000002');
    await page.getByLabel('Expiry').fill('12/25');
    await page.getByLabel('CVC').fill('123');

    await page.getByRole('button', { name: 'Pay now' }).click();

    await expect(page.getByRole('alert')).toContainText('Card declined');
  });
});
不稳定测试的检测与预防

常见原因:

  1. 异步操作中的竞态条件
  2. 依赖时间的测试
  3. 测试之间共享状态
  4. 网络波动
  5. 动画/过渡时间

修复方法:

// ❌ Bad: Fixed timeout
await page.waitForTimeout(2000);

// ✅ Good: Wait for specific condition
await expect(page.getByText('Loaded')).toBeVisible();

// ❌ Bad: Checking exact time
expect(new Date()).toEqual(specificDate);

// ✅ Good: Mock time
jest.useFakeTimers();
jest.setSystemTime(new Date('2024-01-15'));

// ❌ Bad: Depending on animation completion
await page.click('.button');
expect(await page.isVisible('.modal')).toBe(true);

// ✅ Good: Wait for animation
await page.click('.button');
await expect(page.locator('.modal')).toBeVisible();

覆盖率优化

衡量指标
指标目标优先级
行覆盖率80%+中
分支覆盖率75%+高
函数覆盖率90%+中
关键路径覆盖率100%关键
覆盖率配置
// vitest.config.js
export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      exclude: [
        'node_modules/',
        'test/',
        '**/*.d.ts',
        '**/*.config.*',
        '**/index.ts', // barrel files
      ],
      thresholds: {
        branches: 75,
        functions: 80,
        lines: 80,
        statements: 80
      }
    }
  }
});
查找覆盖率盲区
# Generate detailed coverage report
npx vitest run --coverage

# Find untested files
npx vitest run --coverage --reporter=json | jq '.coverageMap | to_entries | map(select(.value.s | values | any(. == 0))) | .[].key'

CI/CD 集成

GitHub Actions
name: Tests
on: [push, pull_request]

jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm test -- --coverage
      - uses: codecov/codecov-action@v4

  e2e-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npm run test:e2e
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: playwright-report
          path: playwright-report/
测试并行化
// vitest.config.js - parallel by default
export default defineConfig({
  test: {
    pool: 'threads',
    poolOptions: {
      threads: {
        singleThread: false
      }
    }
  }
});

// playwright.config.js
export default defineConfig({
  workers: process.env.CI ? 2 : undefined,
  fullyParallel: true
});

反模式

反模式:测试实现细节

表现形式:

// ❌ Testing internal state
expect(component.state.isLoading).toBe(true);

// ❌ Testing private methods
expect(service._calculateHash()).toBe('abc123');

问题所在: 测试与实现耦合,重构时容易失败

替代方案:

// ✅ Test observable behavior
expect(screen.getByRole('progressbar')).toBeInTheDocument();

// ✅ Test public interface
expect(service.getHash()).toBe('abc123');
反模式:过度模拟

表现形式:

// ❌ Mocking everything
jest.mock('../utils/format');
jest.mock('../utils/validate');
jest.mock('../utils/transform');

问题所在: 即使真实代码已损坏,测试仍会通过

替代方案: 仅在系统边界(API、数据库、外部服务)进行模拟

反模式:对不稳定测试听之任之

表现形式:“那个测试不稳定,跳过吧”

问题所在: 不稳定的测试表明存在真实问题(竞态条件、时序问题)

替代方案: 修复不稳定因素,或在修复期间将测试隔离

反模式:覆盖率作秀

表现形式:

// ❌ Testing for coverage, not behavior
it('covers the function', () => {
  myFunction();
  // No assertions!
});

问题所在: 100% 覆盖率,但毫无信心

替代方案: 每项测试都应断言有意义的行为

快速命令

# Run all tests
npm test

# Run with coverage
npm test -- --coverage

# Run specific file
npm test -- src/utils/format.test.ts

# Run in watch mode
npm test -- --watch

# Run E2E tests
npx playwright test

# Run E2E with UI
npx playwright test --ui

# Debug E2E test
npx playwright test --debug

# Update snapshots
npm test -- -u

参考文件

  • references/test-strategy.md - 全面的测试策略框架
  • references/framework-comparison.md - 详细的框架对比
  • references/coverage-patterns.md - 覆盖率优化技巧
  • references/ci-integration.md - CI/CD 流水线配置

涵盖内容:测试策略 | 单元测试 | 集成测试 | 端到端测试 | 覆盖率 | CI/CD | 不稳定测试调试

搭配使用:security-auditor(安全测试)| performance-engineer(负载测试)| code-reviewer(测试质量)

评论

支持 Markdown