mcpField
T
test-specialist

test-specialist

github.com/ailabs-393测试与质量460902 次安装50 周前更新
代码仓库

关于 test-specialist

面向 JavaScript/TypeScript 项目的测试技能,涵盖测试设计、系统化调试和覆盖率分析,并提供识别未测试代码的脚本。

主要功能

  1. 01单元、集成与端到端测试设计
  2. 02结构化缺陷复现与根因分析
  3. 03安全、性能及边界情况检查
  4. 04覆盖率与未测试文件分析脚本
  5. 05测试维护和质量实践

适用场景

  1. 01为新功能编写测试并采用 TDD
  2. 02复现缺陷并验证修复及回归
  3. 03定位覆盖率不足或缺少测试的文件

安装

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

$npx skills add https://github.com/ailabs-393/ai-labs-claude-skills --skill test-specialist

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

包含的文件

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

index.js
package.json
references/
bug_analysis.md
testing_patterns.md
scripts/
analyze_coverage.py
find_untested_code.py

详细介绍

测试专家

概述

将系统化测试方法和调试技术应用于 JavaScript/TypeScript 应用。此技能提供全面的测试策略、缺陷分析框架和自动化工具,用于识别覆盖率缺口和未经测试的代码。

核心能力

1. 编写测试用例

编写全面的测试,覆盖单元、集成和端到端场景。

单元测试方法

使用 AAA 模式(准备-执行-断言)组织测试:

describe('ExpenseCalculator', () => {
  describe('calculateTotal', () => {
    test('sums expense amounts correctly', () => {
      // Arrange
      const expenses = [
        { amount: 100, category: 'food' },
        { amount: 50, category: 'transport' },
        { amount: 25, category: 'entertainment' }
      ];

      // Act
      const total = calculateTotal(expenses);

      // Assert
      expect(total).toBe(175);
    });

    test('handles empty expense list', () => {
      expect(calculateTotal([])).toBe(0);
    });

    test('handles negative amounts', () => {
      const expenses = [
        { amount: 100, category: 'food' },
        { amount: -50, category: 'refund' }
      ];
      expect(calculateTotal(expenses)).toBe(50);
    });
  });
});

核心原则:

  • 每个测试验证一种行为
  • 覆盖正常路径、边界情况和错误条件
  • 使用能说明测试场景的描述性测试名称
  • 保持测试相互独立、彼此隔离
集成测试方法

测试组件如何协同工作,包括数据库、API 和服务之间的交互:

describe('ExpenseAPI Integration', () => {
  beforeAll(async () => {
    await database.connect(TEST_DB_URL);
  });

  afterAll(async () => {
    await database.disconnect();
  });

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

  test('POST /expenses creates expense and updates total', async () => {
    const response = await request(app)
      .post('/api/expenses')
      .send({
        amount: 50,
        category: 'food',
        description: 'Lunch'
      })
      .expect(201);

    expect(response.body).toMatchObject({
      id: expect.any(Number),
      amount: 50,
      category: 'food'
    });

    // Verify database state
    const total = await getTotalExpenses();
    expect(total).toBe(50);
  });
});
端到端测试方法

使用 Playwright 或 Cypress 等工具测试完整的用户工作流:

test('user can track expense from start to finish', async ({ page }) => {
  // Navigate to app
  await page.goto('/');

  // Add new expense
  await page.click('[data-testid="add-expense-btn"]');
  await page.fill('[data-testid="amount"]', '50.00');
  await page.selectOption('[data-testid="category"]', 'food');
  await page.fill('[data-testid="description"]', 'Lunch');
  await page.click('[data-testid="submit"]');

  // Verify expense appears in list
  await expect(page.locator('[data-testid="expense-item"]')).toContainText('Lunch');
  await expect(page.locator('[data-testid="total"]')).toContainText('$50.00');
});
2. 系统化缺陷分析

应用结构化调试方法来识别和修复问题。

五步分析流程
  1. 复现:可靠地复现缺陷

    • 记录触发问题的确切步骤
    • 确定所需的环境/状态
    • 记录预期行为与实际行为
  2. 隔离:缩小问题范围

    • 在代码路径中进行二分查找
    • 创建最小复现案例
    • 移除无关依赖项
  3. 根因分析:确定根本原因

    • 跟踪执行流程
    • 检查假设和前置条件
    • 查看近期更改(git blame)
  4. 实施修复:实现解决方案

    • 先编写失败的测试(TDD)
    • 实施修复
    • 验证测试通过
  5. 验证:确保修复完整

    • 运行完整测试套件
    • 测试边界情况
    • 确认没有引入回归问题
常见缺陷模式

竞态条件:

// Test concurrent operations
test('handles concurrent updates correctly', async () => {
  const promises = Array.from({ length: 100 }, () =>
    incrementExpenseCount()
  );

  await Promise.all(promises);
  expect(getExpenseCount()).toBe(100);
});

Null/Undefined 错误:

// Test null safety
test.each([null, undefined, '', 0, false])
  ('handles invalid input: %p', (input) => {
    expect(() => processExpense(input)).toThrow('Invalid expense');
  });

差一错误:

// Test boundaries explicitly
describe('pagination', () => {
  test('handles empty list', () => {
    expect(paginate([], 1, 10)).toEqual([]);
  });

  test('handles single item', () => {
    expect(paginate([item], 1, 10)).toEqual([item]);
  });

  test('handles last page with partial items', () => {
    const items = Array.from({ length: 25 }, (_, i) => i);
    expect(paginate(items, 3, 10)).toHaveLength(5);
  });
});
3. 识别潜在问题

主动识别问题,避免其演变为缺陷。

安全漏洞

测试常见的安全问题:

describe('security', () => {
  test('prevents SQL injection', async () => {
    const malicious = "'; DROP TABLE expenses; --";
    await expect(
      searchExpenses(malicious)
    ).resolves.not.toThrow();
  });

  test('sanitizes XSS in descriptions', () => {
    const xss = '<script>alert("xss")</script>';
    const expense = createExpense({ description: xss });
    expect(expense.description).not.toContain('<script>');
  });

  test('requires authentication for expense operations', async () => {
    await request(app)
      .post('/api/expenses')
      .send({ amount: 50 })
      .expect(401);
  });
});
性能问题

测试性能问题:

test('processes large expense list efficiently', () => {
  const largeList = Array.from({ length: 10000 }, (_, i) => ({
    amount: i,
    category: 'test'
  }));

  const start = performance.now();
  const total = calculateTotal(largeList);
  const duration = performance.now() - start;

  expect(duration).toBeLessThan(100); // Should complete in <100ms
  expect(total).toBe(49995000);
});
逻辑错误

使用参数化测试捕获边界情况:

test.each([
  // [input, expected, description]
  [[10, 20, 30], 60, 'normal positive values'],
  [[0, 0, 0], 0, 'all zeros'],
  [[-10, 20, -5], 5, 'mixed positive and negative'],
  [[0.1, 0.2], 0.3, 'decimal precision'],
  [[Number.MAX_SAFE_INTEGER], Number.MAX_SAFE_INTEGER, 'large numbers'],
])('calculateTotal(%p) = %p (%s)', (amounts, expected, description) => {
  const expenses = amounts.map(amount => ({ amount, category: 'test' }));
  expect(calculateTotal(expenses)).toBeCloseTo(expected);
});
4. 测试覆盖率分析

使用自动化工具识别测试覆盖率缺口。

查找未经测试的代码

运行提供的脚本,查找没有对应测试的源文件:

python3 scripts/find_untested_code.py src

脚本将:

  • 扫描源目录中的所有代码文件
  • 找出没有对应测试文件的文件
  • 按类型分类未经测试的文件(组件、服务、工具等)
  • 优先列出最需要测试的文件

解读:

  • API/服务:高优先级——测试业务逻辑和数据操作
  • 模型:高优先级——测试数据验证和转换
  • Hooks:中优先级——测试有状态行为
  • 组件:中优先级——测试复杂的 UI 逻辑
  • 工具:低优先级——根据需要测试复杂函数
分析覆盖率报告

生成覆盖率报告后,运行覆盖率分析脚本:

# Generate coverage (using Jest example)
npm test -- --coverage

# Analyze coverage gaps
python3 scripts/analyze_coverage.py coverage/coverage-final.json

该脚本会识别:

  • 覆盖率低于阈值(默认 80%)的文件
  • 语句、分支和函数覆盖率百分比
  • 需要优先改进的文件

覆盖率目标:

  • 关键路径:覆盖率达到 90% 以上
  • 业务逻辑:覆盖率达到 85% 以上
  • UI 组件:覆盖率达到 75% 以上
  • 工具:覆盖率达到 70% 以上
5. 测试维护与质量

确保测试始终有价值且易于维护。

测试代码质量原则

DRY(不要重复自己):

// Extract common setup
function createTestExpense(overrides = {}) {
  return {
    amount: 50,
    category: 'food',
    description: 'Test expense',
    date: new Date('2024-01-01'),
    ...overrides
  };
}

test('filters by category', () => {
  const expenses = [
    createTestExpense({ category: 'food' }),
    createTestExpense({ category: 'transport' }),
  ];
  // ...
});

清晰的测试数据:

// Bad: Magic numbers
expect(calculateDiscount(100, 0.15)).toBe(85);

// Good: Named constants
const ORIGINAL_PRICE = 100;
const DISCOUNT_RATE = 0.15;
const EXPECTED_PRICE = 85;
expect(calculateDiscount(ORIGINAL_PRICE, DISCOUNT_RATE)).toBe(EXPECTED_PRICE);

避免测试之间相互依赖:

// Bad: Tests depend on execution order
let sharedState;
test('test 1', () => {
  sharedState = { value: 1 };
});
test('test 2', () => {
  expect(sharedState.value).toBe(1); // Depends on test 1
});

// Good: Independent tests
test('test 1', () => {
  const state = { value: 1 };
  expect(state.value).toBe(1);
});
test('test 2', () => {
  const state = { value: 1 };
  expect(state.value).toBe(1);
});

工作流决策树

按照此决策树确定测试方法:

  1. 是否要添加新功能?

    • 是 → 先编写测试(TDD)
      • 编写一个失败的测试
      • 实现功能
      • 验证测试通过
      • 重构
    • 否 → 转到第 2 步
  2. 是否要修复 Bug?

    • 是 → 应用 Bug 分析流程
      • 重现 Bug
      • 编写一个能展示该 Bug 的失败测试
      • 修复实现
      • 验证测试通过
    • 否 → 转到第 3 步
  3. 是否要提高测试覆盖率?

    • 是 → 使用覆盖率工具
      • 运行 find_untested_code.py 识别覆盖缺口
      • 对覆盖率报告运行 analyze_coverage.py
      • 优先处理关键路径
      • 为未测试的代码编写测试
    • 否 → 转到第 4 步
  4. 是否要分析代码质量?

    • 是 → 进行系统化审查
      • 检查安全漏洞
      • 测试边界情况和错误处理
      • 验证性能特征
      • 审查错误处理

测试框架和工具

推荐技术栈

单元/集成测试:

  • 使用 Jest 或 Vitest 作为测试运行器
  • 使用 Testing Library 测试 React 组件
  • 使用 Supertest 测试 API
  • 使用 MSW (Mock Service Worker) 模拟 API

端到端测试:

  • 使用 Playwright 或 Cypress
  • 页面对象模型模式

覆盖率:

  • Istanbul(内置于 Jest/Vitest)
  • JSON 格式的覆盖率报告
运行测试
# Run all tests
npm test

# Run with coverage
npm test -- --coverage

# Run specific test file
npm test -- ExpenseCalculator.test.ts

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

# Run E2E tests
npm run test:e2e

参考文档

如需了解详细模式和技术,请参阅:

  • references/testing_patterns.md - 全面的测试模式、最佳实践和代码示例
  • references/bug_analysis.md - 深入的 Bug 分析框架、常见 Bug 模式和调试技术

这些参考资料包含大量示例和高级技术。遇到以下情况时,请加载这些资料:

  • 处理复杂的测试场景
  • 需要实现特定模式
  • 调试异常问题
  • 针对特定情况寻求最佳实践

脚本

analyze_coverage.py

分析 Jest/Istanbul 覆盖率报告以识别覆盖缺口:

python3 scripts/analyze_coverage.py [coverage-file]

如果未指定覆盖率文件位置,会自动查找常见位置。

输出:

  • 低于覆盖率阈值的文件
  • 语句、分支和函数覆盖率百分比
  • 需要改进的优先处理文件
find_untested_code.py

查找没有对应测试文件的源文件:

python3 scripts/find_untested_code.py [src-dir] [--pattern test|spec]

输出:

  • 源文件和测试文件总数
  • 测试文件覆盖率百分比
  • 按类型分类的未测试文件(API、服务、组件等)
  • 优先级建议

最佳实践摘要

  1. 添加新功能时,先编写测试(TDD)
  2. 测试行为,而非实现——重构后测试也应继续有效
  3. 保持测试独立——测试之间不要共享状态
  4. 使用描述性名称——测试名称应说明测试场景
  5. 覆盖边界情况——空值、空内容、边界值、错误条件
  6. 模拟外部依赖——测试应快速且可靠
  7. 保持较高覆盖率——关键代码的覆盖率应达到 80% 以上
  8. 立即修复失败的测试——绝不提交无法通过的测试
  9. 重构测试——采用与生产代码相同的质量标准
  10. 使用工具——自动化覆盖率分析和缺口识别

评论

支持 Markdown