test-specialist

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Test Specialist

测试专家

Overview

概述

Apply systematic testing methodologies and debugging techniques to JavaScript/TypeScript applications. This skill provides comprehensive testing strategies, bug analysis frameworks, and automated tools for identifying coverage gaps and untested code.
为JavaScript/TypeScript应用应用系统化测试方法论与调试技巧。该技能提供全面的测试策略、Bug分析框架,以及用于识别覆盖缺口和未测试代码的自动化工具。

Core Capabilities

核心能力

1. Writing Test Cases

1. 编写测试用例

Write comprehensive tests covering unit, integration, and end-to-end scenarios.
编写覆盖单元、集成与端到端场景的全面测试。

Unit Testing Approach

单元测试方法

Structure tests using the AAA pattern (Arrange-Act-Assert):
typescript
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);
    });
  });
});
Key principles:
  • Test one behavior per test
  • Cover happy path, edge cases, and error conditions
  • Use descriptive test names that explain the scenario
  • Keep tests independent and isolated
使用AAA模式(准备-执行-断言)构建测试:
typescript
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);
    });
  });
});
核心原则:
  • 每个测试仅验证一种行为
  • 覆盖正常流程、边界情况与错误场景
  • 使用能说明测试场景的描述性命名
  • 保持测试独立与隔离

Integration Testing Approach

集成测试方法

Test how components work together, including database, API, and service interactions:
typescript
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);
  });
});
测试组件间的协作,包括数据库、API与服务交互:
typescript
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);
  });
});

End-to-End Testing Approach

端到端测试方法

Test complete user workflows using tools like Playwright or Cypress:
typescript
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');
});
使用Playwright或Cypress等工具测试完整用户流程:
typescript
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. Systematic Bug Analysis

2. 系统化Bug分析

Apply structured debugging methodology to identify and fix issues.
应用结构化调试方法论识别并修复问题。

Five-Step Analysis Process

五步分析流程

  1. Reproduction: Reliably reproduce the bug
    • Document exact steps to trigger
    • Identify required environment/state
    • Note expected vs actual behavior
  2. Isolation: Narrow down the problem
    • Binary search through code path
    • Create minimal reproduction case
    • Remove unrelated dependencies
  3. Root Cause Analysis: Determine underlying cause
    • Trace execution flow
    • Check assumptions and preconditions
    • Review recent changes (git blame)
  4. Fix Implementation: Implement solution
    • Write failing test first (TDD)
    • Implement the fix
    • Verify test passes
  5. Validation: Ensure completeness
    • Run full test suite
    • Test edge cases
    • Verify no regressions
  1. 复现:稳定复现Bug
    • 记录触发Bug的精确步骤
    • 识别所需环境/状态
    • 记录预期与实际行为差异
  2. 隔离:缩小问题范围
    • 通过二分法排查代码路径
    • 创建最小复现案例
    • 移除无关依赖
  3. 根因分析:确定根本原因
    • 追踪执行流程
    • 检查假设与前置条件
    • 查看最近变更记录(git blame)
  4. 修复实现:实施解决方案
    • 先编写失败的测试(测试驱动开发TDD)
    • 实现修复
    • 验证测试通过
  5. 验证:确保修复完整
    • 运行全量测试套件
    • 测试边界情况
    • 确认无回归问题

Common Bug Patterns

常见Bug模式

Race Conditions:
typescript
// 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 Errors:
typescript
// Test null safety
test.each([null, undefined, '', 0, false])
  ('handles invalid input: %p', (input) => {
    expect(() => processExpense(input)).toThrow('Invalid expense');
  });
Off-by-One Errors:
typescript
// 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);
  });
});
竞态条件:
typescript
// Test concurrent operations
test('handles concurrent updates correctly', async () => {
  const promises = Array.from({ length: 100 }, () =>
    incrementExpenseCount()
  );

  await Promise.all(promises);
  expect(getExpenseCount()).toBe(100);
});
空值/未定义错误:
typescript
// Test null safety
test.each([null, undefined, '', 0, false])
  ('handles invalid input: %p', (input) => {
    expect(() => processExpense(input)).toThrow('Invalid expense');
  });
差一错误:
typescript
// 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. Identifying Potential Issues

3. 识别潜在问题

Proactively identify issues before they become bugs.
在问题演变为Bug前主动识别风险。

Security Vulnerabilities

安全漏洞

Test for common security issues:
typescript
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);
  });
});
测试常见安全问题:
typescript
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);
  });
});

Performance Issues

性能问题

Test for performance problems:
typescript
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);
});
测试性能瓶颈:
typescript
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);
});

Logic Errors

逻辑错误

Use parameterized tests to catch edge cases:
typescript
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);
});
使用参数化测试捕获边界情况:
typescript
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. Test Coverage Analysis

4. 测试覆盖率分析

Use automated tools to identify gaps in test coverage.
使用自动化工具识别测试覆盖缺口。

Finding Untested Code

查找未测试代码

Run the provided script to identify source files without tests:
bash
python3 scripts/find_untested_code.py src
The script will:
  • Scan source directory for all code files
  • Identify which files lack corresponding test files
  • Categorize untested files by type (components, services, utils, etc.)
  • Prioritize files that need testing most
Interpretation:
  • API/Services: High priority - test business logic and data operations
  • Models: High priority - test data validation and transformations
  • Hooks: Medium priority - test stateful behavior
  • Components: Medium priority - test complex UI logic
  • Utils: Low priority - test as needed for complex functions
运行以下脚本识别无对应测试的源码文件:
bash
python3 scripts/find_untested_code.py src
该脚本将:
  • 扫描源码目录下所有代码文件
  • 识别缺少对应测试文件的文件
  • 按类型(组件、服务、工具类等)分类未测试文件
  • 优先标记最需要测试的文件
优先级说明:
  • API/服务:高优先级 - 测试业务逻辑与数据操作
  • 模型:高优先级 - 测试数据验证与转换
  • Hooks:中优先级 - 测试有状态行为
  • 组件:中优先级 - 测试复杂UI逻辑
  • 工具类:低优先级 - 仅在函数复杂时测试

Analyzing Coverage Reports

分析覆盖率报告

Run the coverage analysis script after generating coverage:
bash
undefined
生成覆盖率数据后运行分析脚本:
bash
undefined

Generate coverage (using Jest example)

生成覆盖率报告(以Jest为例)

npm test -- --coverage
npm test -- --coverage

Analyze coverage gaps

分析覆盖缺口

python3 scripts/analyze_coverage.py coverage/coverage-final.json

The script identifies:
- Files below coverage threshold (default 80%)
- Statement, branch, and function coverage percentages
- Priority files to improve

**Coverage targets:**
- Critical paths: 90%+ coverage
- Business logic: 85%+ coverage
- UI components: 75%+ coverage
- Utilities: 70%+ coverage
python3 scripts/analyze_coverage.py coverage/coverage-final.json

该脚本将识别:
- 覆盖率低于阈值的文件(默认80%)
- 语句、分支与函数覆盖率百分比
- 需要优先优化的文件

**覆盖率目标:**
- 关键路径:90%+ 覆盖率
- 业务逻辑:85%+ 覆盖率
- UI组件:75%+ 覆盖率
- 工具类:70%+ 覆盖率

5. Test Maintenance and Quality

5. 测试维护与质量

Ensure tests remain valuable and maintainable.
确保测试持续具备价值且易于维护。

Test Code Quality Principles

测试代码质量原则

DRY (Don't Repeat Yourself):
typescript
// 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' }),
  ];
  // ...
});
Clear test data:
typescript
// 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);
Avoid test interdependence:
typescript
// 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);
});
DRY(避免重复):
typescript
// 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' }),
  ];
  // ...
});
清晰的测试数据:
typescript
// 不良示例:魔法数字
expect(calculateDiscount(100, 0.15)).toBe(85);

// 良好示例:命名常量
const ORIGINAL_PRICE = 100;
const DISCOUNT_RATE = 0.15;
const EXPECTED_PRICE = 85;
expect(calculateDiscount(ORIGINAL_PRICE, DISCOUNT_RATE)).toBe(EXPECTED_PRICE);
避免测试依赖:
typescript
// 不良示例:测试依赖执行顺序
let sharedState;
test('test 1', () => {
  sharedState = { value: 1 };
});
test('test 2', () => {
  expect(sharedState.value).toBe(1); // 依赖test 1的执行
});

// 良好示例:独立测试
test('test 1', () => {
  const state = { value: 1 };
  expect(state.value).toBe(1);
});
test('test 2', () => {
  const state = { value: 1 };
  expect(state.value).toBe(1);
});

Workflow Decision Tree

工作流决策树

Follow this decision tree to determine the testing approach:
  1. Adding new functionality?
    • Yes → Write tests first (TDD)
      • Write failing test
      • Implement feature
      • Verify test passes
      • Refactor
    • No → Go to step 2
  2. Fixing a bug?
    • Yes → Apply bug analysis process
      • Reproduce the bug
      • Write failing test demonstrating bug
      • Fix the implementation
      • Verify test passes
    • No → Go to step 3
  3. Improving test coverage?
    • Yes → Use coverage tools
      • Run
        find_untested_code.py
        to identify gaps
      • Run
        analyze_coverage.py
        on coverage reports
      • Prioritize critical paths
      • Write tests for untested code
    • No → Go to step 4
  4. Analyzing code quality?
    • Yes → Systematic review
      • Check for security vulnerabilities
      • Test edge cases and error handling
      • Verify performance characteristics
      • Review error handling
遵循以下决策树选择测试方法:
  1. 是否新增功能?
    • 是 → 先编写测试(TDD)
      • 编写失败的测试
      • 实现功能
      • 验证测试通过
      • 重构
    • 否 → 进入步骤2
  2. 是否修复Bug?
    • 是 → 应用Bug分析流程
      • 复现Bug
      • 编写能复现Bug的失败测试
      • 实现修复
      • 验证测试通过
    • 否 → 进入步骤3
  3. 是否提升测试覆盖率?
    • 是 → 使用覆盖率工具
      • 运行
        find_untested_code.py
        识别缺口
      • 对覆盖率报告运行
        analyze_coverage.py
      • 优先处理关键路径
      • 为未测试代码编写测试
    • 否 → 进入步骤4
  4. 是否分析代码质量?
    • 是 → 系统化评审
      • 检查安全漏洞
      • 测试边界情况与错误处理
      • 验证性能特征
      • 评审错误处理逻辑

Testing Frameworks and Tools

测试框架与工具

Recommended Stack

推荐技术栈

Unit/Integration Testing:
  • Jest or Vitest for test runner
  • Testing Library for React components
  • Supertest for API testing
  • MSW (Mock Service Worker) for API mocking
E2E Testing:
  • Playwright or Cypress
  • Page Object Model pattern
Coverage:
  • Istanbul (built into Jest/Vitest)
  • Coverage reports in JSON format
单元/集成测试:
  • 测试运行器:Jest 或 Vitest
  • React组件测试:Testing Library
  • API测试:Supertest
  • API mocking:MSW (Mock Service Worker)
端到端测试:
  • Playwright 或 Cypress
  • 页面对象模型模式
覆盖率:
  • Istanbul(内置于Jest/Vitest)
  • JSON格式覆盖率报告

Running Tests

运行测试

bash
undefined
bash
undefined

Run all tests

运行所有测试

npm test
npm test

Run with coverage

运行测试并生成覆盖率报告

npm test -- --coverage
npm test -- --coverage

Run specific test file

运行指定测试文件

npm test -- ExpenseCalculator.test.ts
npm test -- ExpenseCalculator.test.ts

Run in watch mode

监听模式运行

npm test -- --watch
npm test -- --watch

Run E2E tests

运行端到端测试

npm run test:e2e
undefined
npm run test:e2e
undefined

Reference Documentation

参考文档

For detailed patterns and techniques, refer to:
  • references/testing_patterns.md
    - Comprehensive testing patterns, best practices, and code examples
  • references/bug_analysis.md
    - In-depth bug analysis framework, common bug patterns, and debugging techniques
These references contain extensive examples and advanced techniques. Load them when:
  • Dealing with complex testing scenarios
  • Need specific pattern implementations
  • Debugging unusual issues
  • Seeking best practices for specific situations
如需详细模式与技巧,请参考:
  • references/testing_patterns.md
    - 全面的测试模式、最佳实践与代码示例
  • references/bug_analysis.md
    - 深入的Bug分析框架、常见Bug模式与调试技巧
当遇到以下场景时可查阅这些参考文档:
  • 处理复杂测试场景
  • 需要特定模式的实现方案
  • 调试特殊问题
  • 寻求特定场景的最佳实践

Scripts

脚本

analyze_coverage.py

analyze_coverage.py

Analyze Jest/Istanbul coverage reports to identify gaps:
bash
python3 scripts/analyze_coverage.py [coverage-file]
Automatically finds common coverage file locations if not specified.
Output:
  • Files below coverage threshold
  • Statement, branch, and function coverage percentages
  • Priority files to improve
分析Jest/Istanbul覆盖率报告以识别缺口:
bash
python3 scripts/analyze_coverage.py [coverage-file]
若未指定文件路径,将自动查找常见的覆盖率文件位置。
输出内容:
  • 覆盖率低于阈值的文件
  • 语句、分支与函数覆盖率百分比
  • 需要优先优化的文件

find_untested_code.py

find_untested_code.py

Find source files without corresponding test files:
bash
python3 scripts/find_untested_code.py [src-dir] [--pattern test|spec]
Output:
  • Total source and test file counts
  • Test file coverage percentage
  • Untested files categorized by type (API, services, components, etc.)
  • Recommendations for prioritization
查找无对应测试文件的源码文件:
bash
python3 scripts/find_untested_code.py [src-dir] [--pattern test|spec]
输出内容:
  • 源码与测试文件总数
  • 测试文件覆盖率百分比
  • 按类型分类的未测试文件(API、服务、组件等)
  • 优先级建议

Best Practices Summary

最佳实践总结

  1. Write tests first (TDD) when adding new features
  2. Test behavior, not implementation - tests should survive refactoring
  3. Keep tests independent - no shared state between tests
  4. Use descriptive names - test names should explain the scenario
  5. Cover edge cases - null, empty, boundary values, error conditions
  6. Mock external dependencies - tests should be fast and reliable
  7. Maintain high coverage - 80%+ for critical code
  8. Fix failing tests immediately - never commit broken tests
  9. Refactor tests - apply same quality standards as production code
  10. Use tools - automate coverage analysis and gap identification
  1. 新增功能时先写测试(TDD)
  2. 测试行为而非实现 - 测试需能在重构后依然有效
  3. 保持测试独立 - 测试间无共享状态
  4. 使用描述性命名 - 测试名称需说明场景
  5. 覆盖边界情况 - 空值、空集合、边界值、错误场景
  6. Mock外部依赖 - 测试需快速且可靠
  7. 维持高覆盖率 - 关键代码覆盖率不低于80%
  8. 立即修复失败测试 - 绝不提交失败的测试
  9. 重构测试代码 - 应用与生产代码相同的质量标准
  10. 使用工具自动化 - 自动分析覆盖缺口与未测试代码