testing-quality-assurance-engineer
Skill4 starsChanged 8 months agoMITView on GitHub
AI Agent 测试质量保证专家 - 单元测试、集成测试、E2E 测试、测试覆盖率、质量门禁
- Reads credentials
In this repository14 other files it gives its agents.
- agent-architectSkill
- context-engineerSkill
- debug-diagnosticianSkill
- error-handling-specialistSkill
- harness-engineerSkill
- mcp-integration-specialistSkill
- performance-optimizerSkill
- prompt-engineerSkill
- security-compliance-officerSkill
- skill-developerSkill
- state-management-engineerSkill
- tool-integratorSkill
- ui-component-architectSkill
- verification-engineerSkill
The file
Files
Testing & Quality Assurance Engineer
使用此技能当你需要进行测试策略设计、编写测试用例、建立质量门禁系统、或提升代码质量和测试覆盖率。
目标
产出完整的测试和质量保证方案,包括单元测试、集成测试、E2E 测试策略、测试覆盖率分析和质量门禁系统。
核心能力
1. 单元测试最佳实践
// 基于 Claude Code的测试模式
describe('TaskManager', () => {
let taskManager: TaskManager
let mockState: MockState
beforeEach(() => {
mockState = createMockState()
taskManager = new TaskManager(mockState)
})
afterEach(() => {
jest.restoreAllMocks()
})
describe('createTask', () => {
it('should create task with unique id', async () => {
// Arrange
const taskType = 'local_bash'
const description = 'Test task'
// Act
const task = await taskManager.createTask(taskType, description)
// Assert
expect(task.id).toMatch(/^[a-z][0-9a-z]{8}$/)
expect(task.type).toBe(taskType)
expect(task.status).toBe('pending')
})
it('should emit task:created event', async () => {
// Arrange
const eventHandler = jest.fn()
taskManager.on('task:created', eventHandler)
// Act
await taskManager.createTask('local_bash', 'Test')
// Assert
expect(eventHandler).toHaveBeenCalledTimes(1)
expect(eventHandler).toHaveBeenCalledWith(
expect.objectContaining({
type: 'local_bash',
status: 'pending'
})
)
})
it('should handle concurrent task creation', async () => {
// Arrange
const tasks = await Promise.all(
Array.from({ length: 10 }).map(() =>
taskManager.createTask('local_bash', 'Concurrent task')
)
)
// Assert - all IDs should be unique
const ids = tasks.map(t => t.id)
expect(new Set(ids).size).toBe(tasks.length)
})
})
})2. 集成测试模式
// MCP 服务器集成测试
describe('MCP Integration', () => {
let mcpServer: MCPServer
let testClient: MCPClient
beforeAll(async () => {
// 启动真实的 MCP 服务器
mcpServer = await startTestMCPServer({
port: findAvailablePort()
})
// 连接到服务器
testClient = await connectMCPClient(mcpServer.url)
}, 30000) // 增加超时时间
afterAll(async () => {
// 清理资源
await testClient.disconnect()
await mcpServer.shutdown()
})
it('should list available tools', async () => {
// Act
const tools = await testClient.listTools()
// Assert
expect(tools).toHaveLength(expectedToolCount)
expect(tools.map(t => t.name)).toEqual([
'file_read',
'file_write',
'bash',
// ... expected tools
])
})
it('should execute tool and return result', async () => {
// Act
const result = await testClient.callTool('bash', {
command: 'echo "Hello World"'
})
// Assert
expect(result.content).toContain('Hello World')
expect(result.exitCode).toBe(0)
}, 10000)
})3. E2E 测试框架
// Puppeteer/Playwright E2E测试
describe('Agent E2E Flow', () => {
let browser: Browser
let page: Page
beforeAll(async () => {
browser = await puppeteer.launch({
headless: process.env.CI ? true : false,
slowMo: 50 // CI 环境下加速
})
})
afterAll(async () => {
await browser.close()
})
beforeEach(async () => {
page = await browser.newPage()
// 设置视口
await page.setViewport({ width: 1280, height: 720 })
// 导航到应用
await page.goto('http://localhost:3000', {
waitUntil: 'networkidle0'
})
})
afterEach(async () => {
await page.close()
})
it('should complete full conversation flow', async () => {
// Step 1: 输入用户消息
await page.type('[data-testid="chat-input"]', 'Hello, can you help me?')
await page.click('[data-testid="send-button"]')
// Step 2: 等待 Agent 响应
await page.waitForSelector('[data-testid="assistant-message"]', {
timeout: 30000
})
// Step 3: 验证响应显示
const messages = await page.$$('[data-testid="message"]')
expect(messages.length).toBe(2) // 用户 + 助手
// Step 4: 截图对比(视觉回归测试)
const screenshot = await page.screenshot()
expect(screenshot).toMatchImageSnapshot({
customSnapshotsDir: '__snapshots__/e2e'
})
}, 60000)
it('should handle tool execution visualization', async () => {
// 发送需要执行工具的消息
await page.type('[data-testid="chat-input"]', 'List files in current directory')
await page.click('[data-testid="send-button"]')
// 等待工具执行 UI
await page.waitForSelector('[data-testid="tool-execution"]', {
timeout: 30000
})
// 验证工具执行进度显示
const progressBars = await page.$$('[data-testid="progress-bar"]')
expect(progressBars.length).toBeGreaterThan(0)
// 等待完成
await page.waitForSelector('[data-testid="tool-result"]', {
timeout: 30000
})
// 验证结果显示
const result = await page.$eval(
'[data-testid="tool-result"]',
el => el.textContent
)
expect(result).toBeTruthy()
}, 60000)
})4. 测试覆盖率分析
interface CoverageReport {
total: {
statements: number
branches: number
functions: number
lines: number
}
covered: {
statements: number
branches: number
functions: number
lines: number
}
coverage: {
statements: number // percentage
branches: number
functions: number
lines: number
}
files: Array<{
path: string
coverage: number
uncoveredLines: number[]
}>
}
function analyzeCoverage(report: CoverageReport): CoverageAnalysis {
const gaps: string[] = []
// 识别覆盖率低的文件
for (const file of report.files) {
if (file.coverage < 80) {
gaps.push(`${file.path}: ${file.coverage}% (${file.uncoveredLines.length} uncovered lines)`)
}
}
// 计算加权平均
const weightedAverage = calculateWeightedAverage(report.files)
return {
overall: weightedAverage,
gaps,
recommendations: generateRecommendations(gaps)
}
}
function generateRecommendations(gaps: string[]): string[] {
const recommendations: string[] = []
if (gaps.length > 0) {
recommendations.push('Add tests for uncovered files')
}
const criticalFiles = gaps.filter(g =>
g.includes('src/core/') || g.includes('src/utils/')
)
if (criticalFiles.length > 0) {
recommendations.push('Prioritize core module coverage')
}
return recommendations
}5. 质量门禁系统
interface QualityGate {
name: string
check: () => Promise<boolean>
threshold?: number
critical: boolean
}
class QualityGateKeeper {
private gates: QualityGate[] = [
{
name: 'Test Coverage',
check: async () => {
const coverage = await getCoverage()
return coverage.lines >= 80
},
threshold: 80,
critical: true
},
{
name: 'No Console Errors',
check: async () => {
const errors = await getConsoleErrors()
return errors.length === 0
},
critical: false
},
{
name: 'Bundle Size',
check: async () => {
const size = await getBundleSize()
return size < 5 * 1024 * 1024 // 5MB
},
threshold: 5 * 1024 * 1024,
critical: false
},
{
name: 'Performance Budget',
check: async () => {
const metrics = await measurePerformance()
return metrics.fcp < 1500 && metrics.lcp < 2500
},
critical: true
}
]
async runAllGates(): Promise<QualityGateResult[]> {
const results: QualityGateResult[] = []
for (const gate of this.gates) {
try {
const passed = await gate.check()
results.push({
name: gate.name,
passed,
critical: gate.critical
})
} catch (error) {
results.push({
name: gate.name,
passed: false,
error: error instanceof Error ? error.message : 'Unknown error',
critical: gate.critical
})
}
}
return results
}
shouldBlockDeployment(results: QualityGateResult[]): boolean {
return results.some(r => r.critical && !r.passed)
}
}6. 测试数据工厂
// Faker.js风格的测试数据生成
class TestDataFactory {
static task(type: TaskType = 'local_bash'): Task {
return {
id: generateTaskId(type),
type,
status: 'pending',
description: `Test task ${randomId()}`,
startTime: Date.now(),
outputFile: `/tmp/test-${randomId()}.jsonl`,
outputOffset: 0,
notified: false
}
}
static agent(name?: string): Agent {
return {
agentId: name || `test-agent-${randomId()}`,
agentType: 'coding-assistant',
whenToUse: 'For coding tasks',
model: 'claude-sonnet-4-5-20260101',
tools: ['file_read', 'file_write', 'bash'],
disallowedTools: []
}
}
static message(role: 'user' | 'assistant' = 'user'): Message {
return {
role,
content: [{
type: 'text',
text: `Test message ${randomId()}`
}],
timestamp: Date.now()
}
}
static error(code: ErrorCode = 'VALIDATION_ERROR'): ClaudeError {
return createError(code, `Test error ${randomId()}`, {
severity: 'medium',
recoverable: false
})
}
}
// 使用示例
const testTask = TestDataFactory.task('local_agent')
const testAgent = TestDataFactory.agent('my-test-agent')
const testError = TestDataFactory.error('NETWORK_ERROR')工作流程
第一步:测试策略设计 (20 分钟)
- 识别测试金字塔层次
E2E Tests (10%) ↑ Integration Tests (20%) ↑ Unit Tests (70%) - 确定测试优先级
const testPriority = { P0: '核心业务逻辑 - 必须测试', P1: '公共 API - 应该测试', P2: '工具函数 - 建议测试', P3: 'UI 组件 - 可选测试' }
第二步:测试实现 (40 分钟)
- 编写单元测试
- 编写集成测试
- 编写 E2E 测试
- 配置覆盖率检查
第三步:质量门禁配置 (15 分钟)
- 定义质量指标
- 设置阈值
- 配置 CI/CD集成
规则
- ✅ 测试应该是确定性的
- ✅ 每个测试只验证一个行为
- ✅ 使用有意义的测试名称
- ✅ 测试应该快速执行
- ✅ 保持测试独立性
- ❌ 不要测试实现细节
- ❌ 不要在测试之间共享状态
- ❌ 不要跳过失败的测试
- ❌ 不要忘记清理测试资源
输出格式
📊 测试覆盖率报告
## 覆盖率总结
| 类型 | 覆盖率 | 目标 | 状态 |
|------|--------|------|------|
| Statements | 85% | 80% | ✅ |
| Branches | 78% | 75% | ✅ |
| Functions | 92% | 85% | ✅ |
| Lines | 86% | 80% | ✅ |
## 未覆盖的关键文件
- src/core/auth.ts (45%)
- src/utils/parser.ts (62%)🏗️ 测试架构图
展示测试层次和依赖关系。
📝 测试用例清单
提供完整的测试用例列表。
🔍 质量门禁结果
## 质量门禁检查结果
- [x] Test Coverage >= 80%
- [x] No Console Errors
- [x] Bundle Size < 5MB
- [x] Performance Budget Met
- [ ] TypeScript Errors (2 errors found)⚠️ 风险评估
| 风险项 | 可能性 | 影响程度 | 缓解措施 |
| 覆盖率虚高 | 中 | 中 | 人工审查关键路径 |
| 测试脆弱 | 高 | 低 | 避免测试实现细节 |
| CI 时间长 | 中 | 中 | 并行执行、缓存依赖 |
交付物清单
最终报告应包含:
- 测试策略文档
- 单元测试套件
- 集成测试套件
- E2E 测试套件
- 测试覆盖率报告
- 质量门禁配置
- CI/CD集成指南
- 测试数据工厂
🤖 AI IDE 开发指南
本部分指导 AI IDE 按照 Claude Code的最佳实践开发测试和质量保证系统。
AI IDE 开发前检查
测试环境验证
必须检查:
- 测试框架已安装 (Jest/Vitest)
- 测试配置文件存在
- 模拟数据工具可用
- CI/CD管道配置
警告信号:
- 缺少测试依赖
- 测试脚本未定义
- 覆盖率报告未配置测试基础设施
必须存在:
- 测试运行器
- 断言库
- Mock 工具
- 覆盖率工具AI IDE 开发中指导
实时测试质量监控
当编写测试代码时,AI IDE应该:
- 测试结构检查
// ✅ 良好的测试结构 describe('UserService', () => { describe('createUser', () => { it('should create user with valid data', async () => { // Arrange // Act // Assert }) it('should throw on duplicate email', async () => { // ... }) }) }) // ❌ 糟糕的测试结构 test('works', async () => { /* ... */ }) // AI IDE 应该:建议使用描述性名称和 AAA 模式 - 断言质量验证
// ✅ 具体的断言 expect(user.email).toBe('test@example.com') expect(user.createdAt).toBeInstanceOf(Date) // ❌ 模糊的断言 expect(result).toBeTruthy() // 不够具体 // AI IDE 应该:建议使用更具体的断言 - 测试隔离检查
// ✅ 隔离的测试 beforeEach(() => { mockRepository.reset() }) // ❌ 共享状态的测试 let sharedState = {} // 测试间共享! // AI IDE 应该:警告并在每个测试前重置
测试覆盖率优化
AI IDE应该提供:
- 覆盖率热点图: 可视化未覆盖的代码
- 智能测试建议: 基于代码分析推荐测试用例
- 回归测试选择: 基于变更选择相关测试
- 性能分析: 识别慢测试并优化
AI IDE 完成后验证
测试质量健康度评分卡
单元测试 (0-10分):
- 覆盖率 > 80%
- 测试速度快 (<100ms/个)
- 断言具体明确
集成测试 (0-10分):
- 关键路径覆盖
- Mock 合理
- 资源清理完整
E2E 测试 (0-10分):
- 用户旅程完整
- 视觉回归正常
- 跨浏览器通过
质量门禁 (0-10分):
- 所有门禁通过
- 阈值合理
- CI/CD集成完整
总分评级:
- 90-100: Excellent (生产就绪)
- 75-89: Good (少量优化)
- 60-74: Fair (需要改进)
- <60: Poor (不建议发布)自动化测试清单
AI IDE应该运行:
# 1. 单元测试
npm run test:unit -- --coverage
# 2. 集成测试
npm run test:integration
# 3. E2E 测试
npm run test:e2e
# 4. 覆盖率检查
nyc check-coverage --lines 80 --branches 75
# 5. 质量门禁
npm run quality-gateAI IDE 常见陷阱检测
🔴 高危问题(必须修复)
// ❌ 测试实现细节而非行为
expect(component.state.count).toBe(5)
// AI IDE 应该:建议测试可见行为而非内部状态
// ❌ 测试之间有依赖
it('should work after previous test', () => {
// 依赖上一个测试的状态
})
// AI IDE 应该:警告并要求测试独立
// ❌ 没有时间相关的清理
beforeEach(() => {
jest.useFakeTimers()
// 但没有 afterEach 恢复
})
// AI IDE 应该:提醒添加清理代码🟡 中等风险(建议优化)
// ⚠️ 测试太慢
it('should do something', async () => {
await sleep(5000) // 不必要的等待
}, 10000)
// AI IDE 应该:建议优化或使用 fake timers
// ⚠️ 断言不够具体
expect(result).toBeDefined()
// AI IDE 应该:建议更具体的断言🟢 低风险(可选改进)
// 💡 可以优化的模式
const user = await createUser({
name: 'John',
email: 'john@example.com'
})
// AI IDE 可以建议:使用测试数据工厂
const user = TestDataFactory.user()AI IDE 代码审查检查清单
在 PR/MR阶段,AI IDE应该自动检查:
单元测试
- 遵循 AAA 模式 (Arrange-Act-Assert)
- 测试名称描述行为
- 断言具体明确
- 无测试间依赖
- 清理资源完整
集成测试
- Mock 外部依赖
- 测试真实交互
- 错误处理覆盖
- 超时配置合理
E2E 测试
- 用户旅程完整
- 等待策略正确
- 截图对比配置
- 跨浏览器测试
覆盖率
- 行覆盖率 >= 80%
- 分支覆盖率 >= 75%
- 关键路径 100% 覆盖
- 新增代码有测试
质量门禁
- 所有关键门禁通过
- 性能预算符合
- 无 TypeScript 错误
- 无 ESLint 警告
AI IDE 集成实现示例
interface TestingRule {
id: string
description: string
severity: 'error' | 'warning' | 'info'
check: (testFiles: TestFile[], sourceFiles: SourceFile[]) => Violation[]
fix?: (violation: Violation) => Fix
}
const rules: TestingRule[] = [
{
id: 'TEST-001',
description: 'Tests must have descriptive names',
severity: 'warning',
check: (testFiles) => {
const vagueNames = findVagueTestNames(testFiles)
if (vagueNames.length > 0) {
return [{
message: 'Test names should describe behavior, not implementation',
location: vagueNames.map(n => n.location)
}]
}
return []
},
fix: () => ({ type: 'suggest_descriptive_name' })
},
{
id: 'TEST-002',
description: 'Tests must be isolated',
severity: 'error',
check: (testFiles) => {
const sharedState = findSharedStateBetweenTests(testFiles)
if (sharedState.length > 0) {
return [{
message: 'Tests should not share state',
location: sharedState.map(s => s.location)
}]
}
return []
},
fix: () => ({ type: 'add_reset_before_each' })
},
{
id: 'TEST-003',
description: 'Assertions should be specific',
severity: 'warning',
check: (testFiles) => {
const vagueAssertions = findVagueAssertions(testFiles)
if (vagueAssertions.length > 0) {
return [{
message: 'Use specific assertions instead of toBeTruthy()',
location: vagueAssertions.map(a => a.location)
}]
}
return []
}
},
{
id: 'TEST-004',
description: 'Tests must clean up resources',
severity: 'error',
check: (testFiles) => {
const missingCleanup = findMissingCleanup(testFiles)
if (missingCleanup.length > 0) {
return [{
message: 'Test does not clean up resources',
location: missingCleanup.map(m => m.location)
}]
}
return []
},
fix: () => ({ type: 'add_after_each_cleanup' })
},
{
id: 'TEST-005',
description: 'New code must have tests',
severity: 'error',
check: (testFiles, sourceFiles) => {
const untestedCode = findUntestedNewCode(testFiles, sourceFiles)
if (untestedCode.length > 0) {
return [{
message: 'New code paths are not covered by tests',
location: untestedCode.map(u => u.location)
}]
}
return []
}
}
// ... 更多规则
]AI IDE 学习路径
为了让 AI IDE更好地理解测试和质量保证规范:
- 第一阶段: 学习单元测试原则和 AAA 模式
- 第二阶段: 理解集成测试和 Mock 策略
- 第三阶段: 掌握 E2E 测试和用户旅程设计
- 第四阶段: 实现覆盖率分析和质量门禁
- 第五阶段: 提供智能测试生成和优化建议
通过学习路径,AI IDE可以成长为能够独立设计和审查测试系统的专家。
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

