skills/test-engineering/SKILL.md
测试工程技能 - TDD流程、测试策略、覆盖率、Mock规范、回归测试。当你涉及测试用例、覆盖率、TDD、Mock、断言、回归测试时必须使用此技能。即使用户只是说"加个测试"或"这个bug要测试",也应触发。
npx skillsauth add 0X6C7879/aegissec test-engineeringInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
[测试核心清单] ① 修Bug必须先写失败测试→修复→测试通过(TDD红绿循环) ② 新功能至少4个用例:正常/边界/异常/权限 ③ Mock外部依赖,不Mock核心逻辑 [测试三禁] ❌空断言(assert无实际检查) ❌注释掉的测试 ❌sleep等待异步(用await/信号量) [命名铁律]
test_<功能>_<场景>_<预期结果>,测试名即文档,读名字就知道测什么
写/改涉及测试的代码时,强制遵守:
await/expectation/信号量等待,❌禁止sleep()/Thread.sleep()等固定等待(固定等待=慢机器上随机失败,快机器上浪费时间,用事件驱动等待才可靠)test_<功能>_<场景>_<预期结果>,如test_login_wrongPassword_returns401扫描项目测试文件:
测试覆盖度评估:
检查测试有效性:
检查测试可维护性:
| 层级 | 占比目标 | 特点 | 验证内容 | |------|----------|------|----------| | 单元测试 | 70% | 快速/隔离/数量多 | 函数逻辑、边界条件、错误处理 | | 集成测试 | 20% | 中速/跨模块 | API端到端、数据库CRUD、服务间交互 | | E2E测试 | 10% | 慢/全链路 | 核心用户流程、关键业务场景 |
回归测试机制:
测试数据管理:
XCTest单元测试:
XCTestCase,命名<被测类>TestssetUp()准备测试环境,tearDown()清理(用addTeardownBlock确保异常时也清理)XCTestExpectation + wait(for:timeout:),❌禁止sleep()(固定等待=慢机器随机失败+快机器浪费时间)sink + expectation,SwiftUI测试用ViewInspector或快照测试XCUITest端到端测试(macOS/iOS UI自动化):
XCUIApplication().launch()启动被测应用accessibilityIdentifier > label > index(identifier最稳定)element.waitForExistence(timeout: 5),❌禁止固定sleep(固定sleep在CI环境下极不稳定,用事件驱动等待)launchArguments.append("--uitesting")macOS特有测试场景:
## 测试审查报告
### 测试覆盖概览
| 模块 | 测试文件 | 用例数 | 覆盖场景 | 缺失场景 | 优先级 |
### 测试质量问题
| # | 文件:行号 | 问题类型 | 描述 | 修复建议 | 优先级 |
### 缺失测试(按风险排序)
| # | 模块/功能 | 缺失测试类型 | 风险 | 建议用例 | 优先级 |
### 测试策略建议
| 类别 | 当前状态 | 建议 | 预期效果 |
### 测试金字塔评估
| 层级 | 当前数量 | 目标占比 | 实际占比 | 调整建议 |
test_<功能>_<场景>_<预期>func TestCreateUser_ValidInput_ReturnsUser(t *testing.T) {
// Arrange
input := CreateUserInput{Name: "test", Email: "[email protected]"}
// Act
user, err := CreateUser(input)
// Assert
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if user.Name != input.Name {
t.Errorf("expected name %q, got %q", input.Name, user.Name)
}
}
// Table-driven tests(Go推荐模式)
func TestParse(t *testing.T) {
tests := []struct {
name string
input string
want int
wantErr bool
}{
{"valid", "42", 42, false},
{"negative", "-1", -1, false},
{"empty", "", 0, true},
{"non-numeric", "abc", 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Parse(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("Parse(%q) error = %v, wantErr %v", tt.input, err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("Parse(%q) = %v, want %v", tt.input, got, tt.want)
}
})
}
}
// HTTP handler测试
func TestGetUser_NotFound_Returns404(t *testing.T) {
req := httptest.NewRequest("GET", "/api/users/999", nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
if w.Code != http.StatusNotFound {
t.Errorf("expected 404, got %d", w.Code)
}
}
import { describe, it, expect, vi, beforeEach } from 'vitest'
describe('createUser', () => {
beforeEach(() => { vi.clearAllMocks() })
it('正常创建返回用户', async () => {
const input = { name: 'test', email: '[email protected]' }
const user = await createUser(input)
expect(user.name).toBe('test')
expect(user.id).toBeDefined()
})
it('缺少必填字段抛出错误', async () => {
await expect(createUser({ name: '' })).rejects.toThrow('name is required')
})
it('重复邮箱返回409', async () => {
vi.spyOn(db, 'findByEmail').mockResolvedValue({ id: 1 })
await expect(createUser({ name: 'a', email: '[email protected]' }))
.rejects.toThrow('email already exists')
})
})
// Vue组件测试
import { mount } from '@vue/test-utils'
import UserCard from '@/components/UserCard.vue'
describe('UserCard', () => {
it('显示用户名', () => {
const wrapper = mount(UserCard, { props: { name: '张三' } })
expect(wrapper.text()).toContain('张三')
})
it('点击删除触发事件', async () => {
const wrapper = mount(UserCard, { props: { name: '张三' } })
await wrapper.find('.delete-btn').trigger('click')
expect(wrapper.emitted('delete')).toBeTruthy()
})
})
# Go
go test ./... -v -race -cover # 全量测试+竞态+覆盖率
go test -run TestCreateUser ./handler/ # 单个测试
go test -coverprofile=cover.out && go tool cover -html=cover.out # 覆盖率报告
# Vitest
npx vitest run # 全量测试
npx vitest run --coverage # 覆盖率
npx vitest run src/utils/time.test.ts # 单个文件
npx vitest --watch # 监听模式
development
WooYun-derived business-logic testing methodology for web apps and APIs. Use when the request involves 支付、退款、订单、越权、认证、授权、价格篡改或业务流程绕过 review, especially black-box probing for price tampering, account takeover, and process bypass flaws.
tools
Escalate privileges on Windows systems using service misconfigurations, DLL hijacking, token manipulation, UAC bypasses, registry exploits, and credential dumping. Use when performing Windows post-exploitation or privilege escalation.
development
Use when performing AD pentest tunneling and pivoting, especially with Ligolo-ng, Chisel, frp, proxychains, SSH forwarding, SOCKS relays, reverse tunnels, or when internal reachability is the main blocker.
development
Threat model, security audit, find vulnerabilities, check security of my app, risk assessment, penetration test prep, analyze attack surface, what could an attacker exploit. Use this skill whenever a user wants holistic security analysis of a codebase, application, or project. MUST be invoked instead of analyzing security yourself — it runs a specialized 8-phase STRIDE workflow producing professional deliverables you cannot generate alone: risk assessment reports, DFD diagrams, threat inventories, attack path validation, mitigation plans, and pentest plans. Trigger on: 威胁建模, 安全评估, 渗透测试, 安全分析, 安全审计, 安全检查, 风险评估. NOT for: fixing one specific bug, adding one security feature (rate limiting, CORS), writing tests, CI/CD setup, or debugging errors.