关于 testing-builder
Testing Builder 是用于自动生成测试的技能,面向希望减少手动编写测试工作的开发者。它分析代码、参考已保存的测试偏好,并生成覆盖边界条件的测试套件,同时保存测试模式供后续使用。
主要功能
- 01分析代码并确定测试类型
- 02读取已保存的测试偏好和模式
- 03生成单元、集成、端到端及回归测试
- 04检查覆盖范围与边界条件
- 05保存测试模式以供后续复用
适用场景
- 01为新函数生成单元测试
- 02为修复后的缺陷创建回归测试
- 03按项目习惯编写 API 集成测试
- 04生成关键用户流程的端到端测试
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill testing-builder安装后在 Claude Code 中输入/testing-builder确认已加载。
附带文件SKILL.json
详细介绍
测试构建器
目的
自动生成测试,并学习你的测试风格。通过让测试变得毫不费力,消除 ADHD 用户在测试方面的障碍:
- 分析代码,了解需要测试的内容
- 从记忆中回忆你的测试模式
- 生成全面的测试套件
- 确保覆盖所有边界情况
- 保存测试模式,供将来使用
面向 ADHD 用户:零阻力——无需手动操作,即可立即创建测试。 面向所有用户:无需繁琐工作,即可获得全面覆盖。 学习系统:随着时间推移,越来越擅长匹配你的测试风格。
激活条件
- 用户说:“编写测试”“测试这个”“添加测试”“覆盖率”
- 修复 Bug 后:“创建回归测试”
- 代码审查时:“需要测试”
- 用户提到:“单元测试”“集成测试”“E2E 测试”
核心工作流
1. 分析代码
步骤 1:确定要测试的内容
// Example: Analyzing a function
function calculateDiscount(price, discountPercent, customerType) {
if (customerType === 'premium') {
discountPercent += 10;
}
return price * (1 - discountPercent / 100);
}
// Identify:
- Function name: calculateDiscount
- Parameters: price, discountPercent, customerType
- Logic branches: premium vs non-premium
- Edge cases: negative values, zero, boundary conditions
- Return type: number
步骤 2:确定所需的测试类型
- 单元测试:纯函数、实用工具、组件
- 集成测试:多个组件协同工作、API 端点
- E2E 测试:完整的用户工作流、关键路径
- 回归测试:针对已修复的特定 Bug
2. 回忆测试模式
向 context-manager 查询:
search memories:
- Type: PREFERENCE, PROCEDURE
- Tags: testing, test-framework, test-style
- Project: current project
回忆内容:
- 测试框架(Jest、Vitest、Pytest 等)
- 断言风格(expect、assert、should)
- 测试结构(describe/it、test 块)
- Mock 模式(jest.mock、vi.mock)
- 覆盖率要求
- 文件命名约定
记忆示例:
PREFERENCE: Testing style for BOOSTBOX
- Framework: Vitest
- Assertion: expect()
- Structure: describe/it blocks
- Coverage: Minimum 80%
- File naming: {filename}.test.js
3. 生成测试套件
单元测试模板:
import { describe, it, expect } from 'vitest';
import { calculateDiscount } from './discount';
describe('calculateDiscount', () => {
describe('basic calculations', () => {
it('should calculate discount correctly for regular customers', () => {
const result = calculateDiscount(100, 10, 'regular');
expect(result).toBe(90);
});
it('should add bonus discount for premium customers', () => {
const result = calculateDiscount(100, 10, 'premium');
expect(result).toBe(80); // 10% + 10% bonus
});
});
describe('edge cases', () => {
it('should handle zero discount', () => {
const result = calculateDiscount(100, 0, 'regular');
expect(result).toBe(100);
});
it('should handle 100% discount', () => {
const result = calculateDiscount(100, 100, 'regular');
expect(result).toBe(0);
});
it('should handle zero price', () => {
const result = calculateDiscount(0, 10, 'regular');
expect(result).toBe(0);
});
});
describe('invalid inputs', () => {
it('should handle negative price', () => {
const result = calculateDiscount(-100, 10, 'regular');
expect(result).toBeLessThan(0);
});
it('should handle invalid customer type', () => {
const result = calculateDiscount(100, 10, 'unknown');
expect(result).toBe(90); // Falls back to regular
});
});
});
集成测试模板(API 端点):
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import request from 'supertest';
import { app } from './app';
import { db } from './db';
describe('POST /api/users', () => {
beforeAll(async () => {
await db.connect();
});
afterAll(async () => {
await db.disconnect();
});
it('should create a new user', async () => {
const response = await request(app)
.post('/api/users')
.send({
email: '[email protected]',
name: 'Test User'
});
expect(response.status).toBe(201);
expect(response.body).toHaveProperty('id');
expect(response.body.email).toBe('[email protected]');
});
it('should reject duplicate email', async () => {
await request(app).post('/api/users').send({
email: '[email protected]',
name: 'User 1'
});
const response = await request(app)
.post('/api/users')
.send({
email: '[email protected]',
name: 'User 2'
});
expect(response.status).toBe(409);
expect(response.body.error).toContain('already exists');
});
it('should validate required fields', async () => {
const response = await request(app)
.post('/api/users')
.send({
name: 'No Email'
});
expect(response.status).toBe(400);
expect(response.body.error).toContain('email');
});
});
E2E 测试模板(Playwright):
import { test, expect } from '@playwright/test';
test.describe('User Login Flow', () => {
test('should login successfully with valid credentials', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="email"]', '[email protected]');
await page.fill('[name="password"]', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('.welcome-message')).toContainText('Welcome');
});
test('should show error with invalid credentials', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="email"]', '[email protected]');
await page.fill('[name="password"]', 'wrongpass');
await page.click('button[type="submit"]');
await expect(page.locator('.error-message')).toContainText('Invalid');
await expect(page).toHaveURL('/login');
});
test('should validate required fields', async ({ page }) => {
await page.goto('/login');
await page.click('button[type="submit"]');
await expect(page.locator('[name="email"]:invalid')).toBeVisible();
await expect(page.locator('[name="password"]:invalid')).toBeVisible();
});
});
4. 覆盖率分析
检查已测试的内容:
// Coverage requirements (from memory or defaults)
const requirements = {
statements: 80,
branches: 75,
functions: 90,
lines: 80
};
// Identify untested scenarios
const missing = [
'Error handling for network failures',
'Loading state transitions',
'Concurrent operations',
'Cleanup on unmount'
];
// Generate additional tests for missing coverage
5. 保存测试模式
生成测试后:
# Save testing style to memory
remember: Testing pattern for authentication
Type: PROCEDURE
Tags: testing, authentication, integration
Content: For auth endpoints, always test:
- Valid credentials → success
- Invalid credentials → 401
- Missing fields → 400
- Expired token → 401
- Token refresh flow
特定框架的模式
Jest/Vitest(JavaScript/TypeScript)
import { describe, it, expect, vi, beforeEach } from 'vitest';
describe('Component', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should do something', () => {
expect(result).toBe(expected);
});
});
Mock:
// Mock module
vi.mock('./api', () => ({
fetchData: vi.fn(() => Promise.resolve({ data: [] }))
}));
// Mock implementation
const mockFn = vi.fn().mockImplementation(() => 'value');
Pytest(Python)
import pytest
from mymodule import calculate_discount
class TestCalculateDiscount:
def test_regular_customer(self):
result = calculate_discount(100, 10, 'regular')
assert result == 90
def test_premium_customer(self):
result = calculate_discount(100, 10, 'premium')
assert result == 80
@pytest.mark.parametrize("price,discount,expected", [
(100, 0, 100),
(100, 100, 0),
(0, 10, 0),
])
def test_edge_cases(self, price, discount, expected):
result = calculate_discount(price, discount, 'regular')
assert result == expected
Fixtures:
@pytest.fixture
def sample_user():
return {
'id': 1,
'email': '[email protected]',
'name': 'Test User'
}
def test_user_creation(sample_user):
assert sample_user['email'] == '[email protected]'
React Testing Library
import { render, screen, fireEvent } from '@testing-library/react';
import { expect, test } from 'vitest';
import { UserList } from './UserList';
test('renders user list', () => {
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
render(<UserList users={users} />);
expect(screen.getByText('Alice')).toBeInTheDocument();
expect(screen.getByText('Bob')).toBeInTheDocument();
});
test('handles empty user list', () => {
render(<UserList users={[]} />);
expect(screen.getByText('No users found')).toBeInTheDocument();
});
test('handles user click', () => {
const handleClick = vi.fn();
const users = [{ id: 1, name: 'Alice' }];
render(<UserList users={users} onUserClick={handleClick} />);
fireEvent.click(screen.getByText('Alice'));
expect(handleClick).toHaveBeenCalledWith(users[0]);
});
测试类别
单元测试
测试内容:
- 纯函数
- 实用工具函数
- 组件逻辑
- 类方法
重点关注:
- 输入/输出正确性
- 边界情况
- 错误条件
- 边界值
集成测试
测试内容:
- API 端点
- 数据库操作
- 多个组件协同工作
- 外部服务集成
重点关注:
- 组件交互
- 数据流
- 错误传播
- 事务处理
E2E 测试
测试内容:
- 关键用户流程
- 完整功能
- 多步骤流程
- 跨页面工作流
重点关注:
- 用户视角
- 真实浏览器交互
- 全栈集成
- 类生产环境
回归测试
创建时机:
- 修复 Bug 后
- 防止已知问题
- 历史问题
结构:
describe('Regression: Bug #123 - User map undefined', () => {
it('should handle undefined users array', () => {
// Test that previously failed
const result = renderUserList(undefined);
expect(result).not.toThrow();
});
});
上下文集成
回忆测试偏好
生成测试前:
// Query context-manager
const preferences = searchMemories({
type: 'PREFERENCE',
tags: ['testing', project],
recent: true
});
// Apply preferences
const testConfig = {
framework: preferences.framework || 'vitest',
style: preferences.style || 'describe/it',
coverage: preferences.coverage || 80,
naming: preferences.naming || '{name}.test.js'
};
保存测试模式
生成测试后:
remember: Testing pattern for API endpoints
Type: PROCEDURE
Tags: testing, api, integration
Content: For API endpoints, always include:
1. Happy path (200/201)
2. Validation errors (400)
3. Auth errors (401/403)
4. Not found (404)
5. Server errors (500)
6. Rate limiting (429)
从反馈中学习
如果用户修改了生成的测试:
// Analyze what user changed
const changes = diff(generatedTest, userModifiedTest);
// Save as new pattern if significant
if (changes.significant) {
saveMemory({
type: 'PREFERENCE',
content: `User prefers ${changes.pattern}`,
tags: ['testing', 'user-preference']
});
}
与其他技能集成
Error Debugger
当 error-debugger 修复 Bug 时:
Automatically invoke testing-builder
Create regression test for: {bug_scenario}
Ensure test fails before fix, passes after
Context Manager
加载测试偏好:
Query for PREFERENCE with tags: [testing, test-framework]
Apply framework, style, coverage preferences
保存新模式:
After generating tests for new scenario
Save pattern as PROCEDURE for future reference
Code Quality Auditor
检查测试质量:
After generating tests:
→ Invoke code-quality-auditor
→ Verify test coverage
→ Check for test smells
→ Ensure meaningful assertions
快速参考
测试生成检查清单
✅ 已覆盖正常路径 ✅ 已测试边界情况 ✅ 已处理错误条件 ✅ 已验证无效输入 ✅ 已检查边界值 ✅ 已处理异步操作 ✅ 已正确配置 Mock ✅ 已正确完成清理 ✅ 断言有意义
常见测试模式
| 场景 | 模式 |
|---|---|
| 函数 | 输入 → 输出断言 |
| 组件 | 渲染 → 查询 → 断言 |
| API | 请求 → 响应 → 断言 |
| 异步 | await → Promise → 结果 |
| 错误 | Try/catch → 错误断言 |
| Mock | 设置 → 调用 → 验证 Mock |
触发短语
- "write tests"
- "test this"
- "add coverage"
- "create regression test"
- "needs tests"
文件位置
- 测试模式:
~/.claude-memories/ (Linux/macOS) or %USERPROFILE%\.claude-memories\ (Windows)procedures/(标记为 "testing") - 测试偏好:
~/.claude-memories/ (Linux/macOS) or %USERPROFILE%\.claude-memories\ (Windows)preferences/(标记为 "testing") - 生成的测试:
{filename}.test.{ext}或tests/{filename}.test.{ext}
成功标准
✅ 在 30 秒内生成测试 ✅ 覆盖全面(默认 80%+) ✅ 包含所有边缘情况 ✅ 测试遵循项目风格 ✅ 无需手动操作 ✅ 保存测试模式以供后续使用
评论
支持 Markdown