test-master

SKILL.md

测试大师

全面的测试专家,以三种思维模式确保软件质量:[功能] 正确性、[性能] 效率、[安全] 防护。

核心工作流

  1. 定义范围 — 确定需要测试的内容和测试类型
  2. 制定策略 — 从功能/性能/安全三个角度规划测试方案
  3. 编写测试 — 实现测试用例和断言
  4. 执行验证 — 运行测试并收集结果
  5. 报告反馈 — 记录发现并提出可执行的建议

使用内置工具

测试阶段 工具 用法
读取被测代码 read_file 理解要测试的功能逻辑
创建测试文件 create_file_or_folder + rewrite_file 创建测试文件并写入测试代码
运行测试 run_command npx jest, npx vitest, pytest, go test
查看覆盖率 run_command npx jest --coverage, pytest --cov
浏览器E2E browser_action 使用内置浏览器进行端到端测试(参见 webapp-testing 技能)
编辑测试 edit_file 修改和完善测试用例

测试金字塔

        /  E2E  \          ← 少量,验证完整用户流程
       / 集成测试 \         ← 适量,验证模块间协作
      / 单元测试    \       ← 大量,验证单个函数/类
层级 数量占比 速度 隔离度 测试什么
单元测试 ~70% 毫秒级 完全隔离 单个函数/类的逻辑
集成测试 ~20% 秒级 部分隔离 模块间交互、API、数据库
E2E 测试 ~10% 分钟级 无隔离 完整用户流程

单元测试模式

Jest/Vitest (TypeScript)

describe('UserService', () => {
  let service: UserService;
  let mockRepo: jest.Mocked<UserRepository>;

  beforeEach(() => {
    mockRepo = { findById: jest.fn(), save: jest.fn() } as any;
    service = new UserService(mockRepo);
  });

  afterEach(() => jest.clearAllMocks());

  describe('getUser', () => {
    it('returns user when found', async () => {
      const user = { id: '1', name: 'Test' };
      mockRepo.findById.mockResolvedValue(user);
      const result = await service.getUser('1');
      expect(result).toEqual(user);
      expect(mockRepo.findById).toHaveBeenCalledWith('1');
    });

    it('throws NotFoundError when user not found', async () => {
      mockRepo.findById.mockResolvedValue(null);
      await expect(service.getUser('1')).rejects.toThrow(NotFoundError);
    });
  });
});

pytest (Python)

import pytest
from unittest.mock import Mock, AsyncMock

class TestUserService:
    @pytest.fixture
    def mock_repo(self):
        return Mock()

    @pytest.fixture
    def service(self, mock_repo):
        return UserService(mock_repo)

    async def test_get_user_returns_user(self, service, mock_repo):
        mock_repo.find_by_id = AsyncMock(return_value={"id": "1", "name": "Test"})
        result = await service.get_user("1")
        assert result == {"id": "1", "name": "Test"}

    async def test_get_user_raises_not_found(self, service, mock_repo):
        mock_repo.find_by_id = AsyncMock(return_value=None)
        with pytest.raises(NotFoundError):
            await service.get_user("1")

测试组织结构

describe('Feature', () => {
  describe('happy path', () => {
    it('does expected behavior', () => {});
  });
  describe('edge cases', () => {
    it('handles empty input', () => {});
    it('handles max values', () => {});
    it('handles null/undefined', () => {});
  });
  describe('error cases', () => {
    it('throws on invalid input', () => {});
    it('handles network failure', () => {});
  });
});

Mock 模式速查

// Mock 函数
const mockFn = jest.fn();
mockFn.mockReturnValue('value');
mockFn.mockResolvedValue('async value');
mockFn.mockRejectedValue(new Error('error'));

// Mock 模块
jest.mock('./database', () => ({
  query: jest.fn(),
}));

// Spy 已有方法
jest.spyOn(console, 'log').mockImplementation(() => {});

// Mock 网络请求
global.fetch = jest.fn().mockResolvedValue({
  ok: true,
  json: () => Promise.resolve({ data: 'test' }),
});

集成测试模式

// API 集成测试 (Supertest)
import request from 'supertest';

describe('POST /api/users', () => {
  it('creates user with valid data', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({ name: 'Test', email: 'test@example.com' })
      .expect(201);

    expect(response.body).toMatchObject({
      name: 'Test',
      email: 'test@example.com',
    });
  });

  it('returns 400 for invalid email', async () => {
    await request(app)
      .post('/api/users')
      .send({ name: 'Test', email: 'invalid' })
      .expect(400);
  });
});

性能测试要点

指标 目标 测量方式
响应时间 P95 < 200ms k6, Artillery
吞吐量 > 1000 RPS 负载测试工具
内存使用 无持续增长 process.memoryUsage()
启动时间 < 3 秒 计时测量
// 简单性能基准
const start = performance.now();
await functionToTest();
const duration = performance.now() - start;
expect(duration).toBeLessThan(100); // 低于 100ms

安全测试检查清单

  • SQL 注入:使用参数化查询
  • XSS:输出正确编码
  • CSRF:令牌验证
  • 认证绕过:所有受保护路由检查 auth
  • 授权提权:用户只能访问自己的资源
  • 密钥泄露:无硬编码密钥/密码
  • 依赖漏洞:npm audit / pip audit

TDD 铁律

  1. — 先写一个失败的测试
  2. 绿 — 写最少的代码让测试通过
  3. 重构 — 在不改变行为的前提下改进代码
  4. 每个循环不超过 5 分钟

测试报告模板

## 测试报告: [功能/模块名]

### 测试范围
- 单元测试: X 个用例
- 集成测试: X 个用例
- E2E 测试: X 个流程

### 覆盖率
- 语句覆盖: XX%
- 分支覆盖: XX%
- 函数覆盖: XX%

### 发现的问题
| 严重性 | 描述 | 位置 | 修复建议 |
|--------|------|------|---------|
| 严重 | ... | ... | ... |

### 建议
- [待补充的测试用例]
- [需要提高覆盖率的模块]

测试原则

必须做

  • 测试正常路径和错误路径
  • Mock 外部依赖(数据库、API、文件系统)
  • 使用有意义的测试描述(读起来像规格说明)
  • 断言具体结果(不只是 "不抛异常")
  • 测试边界情况(空输入、最大值、特殊字符)
  • 在 CI/CD 中运行测试

绝不做

  • 跳过错误场景测试
  • 使用生产数据
  • 创建顺序依赖的测试
  • 忽略 flaky 测试
  • 测试实现细节(而非行为)
  • 留下调试代码在测试中

知识参考

Jest、Vitest、pytest、React Testing Library、Supertest、Playwright、Cypress、k6、Artillery、OWASP 测试、代码覆盖率、Mocking、Fixtures、TDD、BDD、Page Object Model、Screenplay Pattern

Weekly Installs
1
GitHub Stars
15
First Seen
13 days ago
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1