关于 test-specialist
面向 JavaScript/TypeScript 项目的测试技能,涵盖测试设计、系统化调试和覆盖率分析,并提供识别未测试代码的脚本。
主要功能
- 01单元、集成与端到端测试设计
- 02结构化缺陷复现与根因分析
- 03安全、性能及边界情况检查
- 04覆盖率与未测试文件分析脚本
- 05测试维护和质量实践
适用场景
- 01为新功能编写测试并采用 TDD
- 02复现缺陷并验证修复及回归
- 03定位覆盖率不足或缺少测试的文件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ailabs-393/ ai-labs-claude-skills --skill test-specialist安装后在 Claude Code 中输入/test-specialist确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
index.jspackage.jsonreferences/bug_analysis.mdtesting_patterns.mdscripts/analyze_coverage.pyfind_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. 系统化缺陷分析
应用结构化调试方法来识别和修复问题。
五步分析流程
-
复现:可靠地复现缺陷
- 记录触发问题的确切步骤
- 确定所需的环境/状态
- 记录预期行为与实际行为
-
隔离:缩小问题范围
- 在代码路径中进行二分查找
- 创建最小复现案例
- 移除无关依赖项
-
根因分析:确定根本原因
- 跟踪执行流程
- 检查假设和前置条件
- 查看近期更改(git blame)
-
实施修复:实现解决方案
- 先编写失败的测试(TDD)
- 实施修复
- 验证测试通过
-
验证:确保修复完整
- 运行完整测试套件
- 测试边界情况
- 确认没有引入回归问题
常见缺陷模式
竞态条件:
// 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);
});
工作流决策树
按照此决策树确定测试方法:
-
是否要添加新功能?
- 是 → 先编写测试(TDD)
- 编写一个失败的测试
- 实现功能
- 验证测试通过
- 重构
- 否 → 转到第 2 步
- 是 → 先编写测试(TDD)
-
是否要修复 Bug?
- 是 → 应用 Bug 分析流程
- 重现 Bug
- 编写一个能展示该 Bug 的失败测试
- 修复实现
- 验证测试通过
- 否 → 转到第 3 步
- 是 → 应用 Bug 分析流程
-
是否要提高测试覆盖率?
- 是 → 使用覆盖率工具
- 运行
find_untested_code.py识别覆盖缺口 - 对覆盖率报告运行
analyze_coverage.py - 优先处理关键路径
- 为未测试的代码编写测试
- 运行
- 否 → 转到第 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、服务、组件等)
- 优先级建议
最佳实践摘要
- 添加新功能时,先编写测试(TDD)
- 测试行为,而非实现——重构后测试也应继续有效
- 保持测试独立——测试之间不要共享状态
- 使用描述性名称——测试名称应说明测试场景
- 覆盖边界情况——空值、空内容、边界值、错误条件
- 模拟外部依赖——测试应快速且可靠
- 保持较高覆盖率——关键代码的覆盖率应达到 80% 以上
- 立即修复失败的测试——绝不提交无法通过的测试
- 重构测试——采用与生产代码相同的质量标准
- 使用工具——自动化覆盖率分析和缺口识别