mcpField
T
testing

testing

github.com/iuliandita测试与质量1049 次安装今天更新
代码仓库

关于 testing

testing 是用于编写、组织和维护软件测试的技能,覆盖单元、集成、E2E、无障碍和性能测试。它强调按改动选择适量测试,以捕获回归而非追求覆盖率数字。

主要功能

  1. 01覆盖多层测试的指导
  2. 02按需选择适量测试
  3. 03结合现有框架的测试模式
  4. 04诊断不稳定测试
  5. 05夹具、模拟与测试数据指导

适用场景

  1. 01为缺陷修复添加回归测试
  2. 02为功能改动选择测试
  3. 03诊断不稳定或失败的测试
  4. 04规划测试基础设施和覆盖率门槛

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/iuliandita/skills --skill testing

安装后在 Claude Code 中输入/testing确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
agent-hygiene.md
e2e-accessibility.md
language-patterns.md
output-contract.md

详细介绍

测试:编写能捕获真实错误的测试

在单元、集成、端到端(E2E)、无障碍和性能测试各层编写、组织和维护测试。目标是让测试捕获回归问题、记录行为并在 CI 中快速运行,而不是为了抬高覆盖率数字而写测试。

目标版本(2026 年 10 月):

  • Vitest 5.0.5,Jest 30.5.2
  • Playwright 1.64.1,Cypress 16.1.1(Vitest 和 Cypress 均为重大版本升级;请查看迁移说明)
  • pytest 9.2.0,pytest-cov 7.1.0
  • Go 1.27.3(testing 标准库,testing/synctest 正式发布)
  • Rust 1.99.0(cargo test,cargo-nextest 0.9.150)
  • Testing Library 16.3.3(@testing-library/react)
  • axe-core 4.14.0(@axe-core/playwright)
  • Grafana k6 2.3.0

安全检查(2026-10-28):GHSA-2h32-95rg-cppp 是严重的浏览器运行器脚本注入漏洞,可能泄露 API 令牌并导致本地代码 执行。它影响 @vitest/browser >=4.0.17,<4.1.6 和 >=5.0.0-beta.0,<5.0.0-beta.3; 修复版本为 4.1.6 和 5.0.0-beta.3。请让浏览器包与稳定版运行器保持一致,并且在服务运行时不要 打开不可信的运行器 URL。后续严重的浏览器模式安全公告 (GHSA-g8mr-85jm-7xhm、GHSA-p63j-vcc4-9vmv) 将 4.x 的最低版本提高至 4.1.10,将 3.x 的最低版本提高至 3.2.7。10 月 21 日的安全公告再次提高最低版本: GHSA-p458-7748-mcp3(server.fs.deny 绕过 可暴露 .env、密钥和 API 令牌;@vitest/ui/@vitest/browser <5.0.5)以及 GHSA-qxg6-89mg-rqv8(模拟注册代码 注入;@vitest/mocker <4.1.12 和 5.0.x <5.0.3)。最低版本为 5.0.5 和 4.1.12;3.x 没有 修复版本,因此请升级。

适用场景

  • 编写新测试(单元、集成、E2E、无障碍、性能)
  • 调试不稳定或失败的测试
  • 为项目设计测试架构(fixture 策略、工厂模式、测试数据)
  • 在 CI 中搭建测试基础设施(并行化、分片、覆盖率门槛)
  • 选择测试工具或在测试框架之间迁移
  • 选择能保护某项更改的最小测试集
  • 为现有测试套件添加无障碍或视觉回归测试

不适用场景

  • 对已确定的应用瓶颈进行性能分析和优化——请使用 performance-debugging。
  • Broker 确认、重新投递、死信或重放语义——请使用 message-queues。
  • 在代码审查中检查现有测试的质量或正确性——请使用 code-review。
  • 专门的安全测试(渗透测试、OWASP 检查)——请使用 security-audit。
  • 清理冗长或草率的测试代码——请使用 code-simplification。
  • 在测试之外临时浏览网页、抓取内容或与页面交互——请使用宿主提供的浏览工具。
  • CI/CD 流水线架构(测试任务会在流水线中运行,但流水线设计属于 ci-cd 的范畴)——请使用 ci-cd。
  • 引擎层面的数据库测试模式——请使用 databases。
  • 编写或改进大模型提示词——请使用 prompt-generator。
  • 测试之外的基础设施或配置验证——请使用 terraform、ansible 或 kubernetes。
  • AI/ML 模型评估或大模型输出评分——请使用 llm-app-development。
  • 超出应用测试范围的基础设施级负载或混沌测试——集群级混沌测试请使用 kubernetes,流水线集成的负载测试编排请使用 ci-cd。

比例原则

根据改动规模安排测试套件。不要因为想测试每个函数或追求数量而违背这一原则。

  • 每种行为写一个测试,再覆盖现实中可能出现的失败模式(错误输入、边界情况、错误路径)。同一种行为的不同输入应放在一个参数化测试中。
  • 跳过针对胶水代码、接线配置、配置项、getter,以及框架或类型系统已保证的行为的测试。
  • 新增测试超过约 5 个,或测试代码比被测代码还长时,请说明每个测试为何值得保留,否则就删减。
  • 修复 Bug 时:编写一个修复前会失败的回归测试即可,不要写一整套测试。
  • 优先选择数量最少、但在行为出错时仍能失败的测试。

AI 自检

AI 工具总是犯同样的测试错误。返回任何生成的测试代码前,请对照以下清单进行检查:

  • 测试断言的是行为,而非实现;不要测试私有方法或内部状态
  • 每个测试只有一个失败原因(一个断言概念,而非只调用一次 assert);同一种行为的不同输入应放在一个参数化测试中,而不是拆成多个测试
  • 测试数量与改动规模相称(参见“比例原则”);没有近似重复的测试,也没有针对胶水代码或框架行为的测试
  • 测试名称描述场景和预期结果,而非方法名称
  • Mock/Stub 的作用域限于单个测试;不同测试之间不共享可变的 Mock 状态
  • 没有会导致其他机器或 CI 出错的硬编码端口、路径或时间戳
  • 异步测试会正确等待所有 Promise/Future;不会使用未等待的断言
  • 测试数据彼此隔离;每个测试都会创建自己的状态,不依赖测试执行顺序
  • 即使断言失败也会执行清理(使用 afterEach/teardown/t.Cleanup/Drop)
  • 等待异步操作时不使用 sleep() 或固定延迟;改用轮询、重试或基于事件的等待
  • 覆盖率阈值切合实际(行覆盖率 80% 是不错的默认值;100% 并不现实),且没有为了覆盖率而添加测试
  • 提交前已手动检查快照测试(盲目使用 --update 容易引发 Bug)
  • E2E 选择器优先使用角色或可访问名称,data-testid 仅作最后手段;不要使用 CSS 类或 DOM 结构
  • 运行器 API 保持最新:pytest、Vitest、Jest、Playwright 和 Testing Library 示例符合当前运行器行为
  • 已找出不稳定来源:不会在未诊断原因的情况下用重试掩盖非确定性
  • 已应用跨领域 Agent 规范;参见 references/agent-hygiene.md

性能

  • 将快速单元测试与集成测试、浏览器测试和性能测试套件分开。
  • 使用 fixture 和测试数据构建器,避免重复进行耗时的初始化。
  • 只有在隔离共享状态、端口、数据库和时钟后,才进行分片或并行运行。

工作流

复制此清单并跟踪进度:

  • 步骤 1:确定范围并识别现有框架
  • 步骤 2:选择测试层级
  • 步骤 3:编写测试(修复 Bug 时,确认修复前测试会失败)
  • 步骤 4:受影响的检查均已通过;并通过步骤 3 修复了并非由被测行为导致的失败
步骤 1:确定范围

根据上下文:

  • 新功能 -> 测试重要行为,可与代码同时编写,也可在代码之后编写;不要测试每个函数
  • 修复 Bug -> 先编写一个会失败的测试来复现 Bug,再进行修复
  • 现有未测试代码 -> 优先覆盖关键路径,不要追求 100% 覆盖率
  • 测试基础设施 -> 配置运行器、CI 配置和覆盖率门槛

从配置文件(vitest.config.ts、jest.config.*、pyproject.toml、Cargo.toml、*_test.go、playwright.config.ts)中识别项目现有的测试框架,并与其保持一致。没有理由时,不要引入第二个测试运行器。

步骤 2:选择测试层级
层级测试内容速度适用场景
单元测试隔离测试单个函数/模块毫秒纯逻辑、实用工具、数据转换、状态机
集成测试测试多个模块及真实依赖秒API 处理程序、数据库查询、服务边界
端到端测试通过 UI 测试完整的用户流程秒至分钟关键路径、结账流程、身份验证、引导流程
无障碍测试WCAG 合规性、屏幕阅读器兼容性秒此次变更涉及的面向用户的组件/页面
视觉测试截图对比秒样式变更后的 UI 组件
性能测试负载、延迟、吞吐量分钟发布前、架构变更后

测试金字塔关注的是测试所处的位置,而不是配额。 将每项检查放在成本最低、但仍能发现故障的层级;不要为了填满各层而编写测试。颠倒这一原则(用耗时的端到端测试检查单元测试就能覆盖的逻辑),CI 就会耗时 45 分钟,而且大家都会忽略失败。优先选择数量最少、但行为出错时会失败的测试。

步骤 3:编写测试

遵循以下针对不同语言的模式。通用原则:

准备-执行-断言(或前提-执行-结果):

// Arrange: set up test data and dependencies
// Act: call the thing being tested
// Assert: verify the outcome

测试命名:描述场景,而不是函数。

# Bad:  test_calculate_total
# Good: test_calculate_total_applies_discount_when_cart_exceeds_100
# Good: it("returns 401 when token is expired")
步骤 4:验证
  • 运行与变更行为相符的检查,以及仓库要求的所有门禁。按需或在受影响范围需要时运行完整测试套件;不要为可逆且影响较小的编辑添加测试。只有出现新变更、失败或未解决的问题时,才重复或扩大通过的检查。
  • 如果新测试失败的原因与待测行为无关,请修复测试或测试固件,然后返回步骤 3
  • 将覆盖率变化作为发现遗漏行为的信号,而不是目标:新增代码不意味着需要追求某个数字,覆盖率也绝不是添加测试的理由
  • 尽可能在 CI 中运行——本地通过却在 CI 中失败的测试最糟糕

TDD 工作流

可选。对于行为预先定义明确的缺陷修复和非简单逻辑,可采用红-绿流程。这并不意味着没有测试就不能编写代码,也不意味着每个函数都要有测试。在探索或原型开发时可跳过。

  1. 红:编写一个会失败的测试(确认失败原因正确)
  2. 绿:编写能让测试通过的最少代码(代码不够漂亮也没关系)
  3. 重构:在不改变行为的前提下清理代码(测试仍然通过)

TDD 最适用于:纯函数、数据转换、状态机、API 契约、缺陷复现。

TDD 不适用于:UI 布局、探索性原型开发、与未文档化 API 的集成。


模拟策略

在边界处进行模拟,而不是无处不模拟。过度模拟会让测试通过,但实际代码却已损坏。

模拟对象不要模拟的对象
外部 API(HTTP、gRPC)你自己的纯函数
数据库(单元测试时)数据转换
时间/日期、随机值简单的实用代码
文件系统(不便使用时)被测模块
第三方 SDK标准库函数

尽可能优先使用伪实现,而不是模拟对象。 内存数据库实现比返回预设响应的模拟对象能测试更多真实行为。

用于 TTL/时间依赖测试的可注入时钟 - 传入时钟依赖,而不是直接调用 Date.now() 或 time.Now():

// Production: clock = () => Date.now()
// Test: clock = () => FIXED_TS + offset
function isExpired(createdAt: number, ttlMs: number, clock = Date.now): boolean {
  return clock() - createdAt > ttlMs;
}
// In test: advance virtual time without sleeping
const fakeNow = vi.fn().mockReturnValue(START);
expect(isExpired(START, 1000, fakeNow)).toBe(false);
fakeNow.mockReturnValue(START + 1001);
expect(isExpired(START, 1000, fakeNow)).toBe(true);

对于带缓存的请求,覆盖可观察到的路径(设置相同的参数化测试或顺序测试即可):

  • 缓存未命中:HTTP 边界调用一次,并缓存返回值。
  • 缓存命中:返回缓存值,且不调用 HTTP 边界。
  • TTL 过期:推进注入的时钟或伪时钟,然后断言发生一次刷新,而不是通过休眠等待。

阅读 references/language-patterns.md,了解特定语言的模拟惯用法(Vitest vi.mock、Jest jest.mock、pytest monkeypatch、Go 接口、Rust trait 对象)。


测试数据与 Fixture

工厂模式(首选)

使用合理的默认值和每个测试的单独覆盖项来构建测试数据:

// TypeScript - factory function
function buildUser(overrides: Partial<User> = {}): User {
  return { id: randomUUID(), name: "Test User", email: "[email protected]", ...overrides };
}

// Python - factory function
def build_user(**overrides) -> User:
    defaults = {"id": uuid4(), "name": "Test User", "email": "[email protected]"}
    return User(**(defaults | overrides))
Fixture 规则
  • 每个测试彼此隔离。 共享的可变 Fixture 会导致依赖执行顺序的失败。
  • 优先使用构建器/工厂,而不是原始对象字面量 - 默认值可避免测试脆弱。
  • 数据库 Fixture:使用在每个测试后回滚的事务(pytest db Fixture、Jest beforeEach 中执行回滚)。使用预置的测试数据库优于共享的预发布环境数据。
  • 文件 Fixture:使用临时目录(pytest 中的 tmp_path、Go 中的 os.MkdirTemp、Rust 中的 tempfile)。在拆卸阶段清理。

无障碍测试

自动检测 WCAG 违规。这不能替代手动测试,但能捕获机械性问题(缺少替代文本、ARIA 错误、对比度、键盘陷阱)。

使用 @axe-core/playwright(如果缺少,可使用仓库的包管理器将其添加为开发依赖:npm install -D、pnpm add -D、yarn add -D 或 bun add -d @axe-core/playwright)- 运行 new AxeBuilder({ page }).withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"]).analyze() 并断言违规数为零。将 axe 扫描范围限定在此次更改涉及的页面或组件,而不是每次更改都扫描整个应用。使用 .exclude() 排除已知问题,并将其作为技术债务跟踪,而不是永久豁免。

在此处维护可重复的无障碍回归检查;使用 frontend-design 修复交互设计和语义。将手动键盘和屏幕阅读器覆盖情况与自动扫描结果分开记录。

阅读 references/e2e-accessibility.md,了解 Playwright E2E 模式、视觉回归设置和 CI 无障碍门禁。


性能测试

分为两类:微基准测试(此函数是否足够快?)和负载测试(系统能否处理流量?)。

微基准测试
  • Go:func BenchmarkX(b *testing.B) - 内置于标准库
  • Rust:使用 criterion 执行 cargo bench(criterion = "0.8")
  • JS/TS:vitest bench(在 Vitest 外直接使用 tinybench)
  • Python:pytest-benchmark(一次性测量可用 timeit)
负载测试(k6)

先检测:command -v k6 >/dev/null || echo "k6 missing"。

// k6 load test
import http from "k6/http";
import { check, sleep } from "k6";

export const options = {
  stages: [
    { duration: "30s", target: 50 },   // ramp up
    { duration: "1m",  target: 50 },   // sustain
    { duration: "10s", target: 0 },    // ramp down
  ],
  thresholds: {
    checks: ["rate==1"],              // fail the run when any status check fails
    http_req_duration: ["p(95)<500"],   // 95th percentile under 500ms
  },
};

export default function () {
  const res = http.get("http://localhost:3000/api/health");
  check(res, { "status 200": (r) => r.status === 200 });
  sleep(1);
}

未经明确批准,不要对生产环境运行负载测试。除非有专用基础设施,否则不要在 CI 中运行负载测试。


CI 集成

测试并行化
  • Vitest/Jest:内置工作进程并行机制。Vitest 使用 Vite 的模块图来智能分配测试文件。
  • Playwright:使用 --shard=1/4 将测试拆分到多个 CI 运行器上。使用 --workers=4 在单个运行器内并行执行。
  • pytest:使用 pytest-xdist 和 -n auto,根据 CPU 核心数并行执行。
  • Go:使用 go test -parallel N 并行执行包内测试,使用 -p N 并行执行多个包。
  • Rust:使用 cargo nextest run 实现每个测试进程隔离和并行执行(cargo nextest --version >/dev/null 2>&1 || echo "cargo-nextest missing: cargo install cargo-nextest")。
不稳定测试管理

不稳定测试会削弱信任。应立即修复或隔离。

  1. 识别:持续跟踪测试稳定性(大多数 CI 系统都提供不稳定测试看板)
  2. 隔离:将测试移到不会阻止合并的独立任务中。添加 @flaky 或 skip 标签。
  3. 修复根本原因——各框架中的常见诱因:
    • Playwright/Cypress:导航或动画中的竞态条件。使用 waitForLoadState、 waitForSelector 或 Playwright 的自动等待机制。避免使用 page.waitForTimeout。模拟网络 请求以消除后端波动。为每项测试创建全新的浏览器上下文,避免 Cookie、 存储和 Service Worker 在测试用例之间泄漏。无头模式(CI)与有头模式的渲染时序不同。 首先使用与 CI 相同的无头浏览器、操作系统映像、视口、 字体和工作进程数复现问题;只有在排查渲染差异时才对比 --headed。调整超时前,先检查 CI 运行器上的 CPU、内存和工作进程竞争情况。
    • Vitest/Jest:测试文件之间共享模块状态。使用 --pool forks(Vitest)或 --runInBand(Jest)进行隔离。检查是否有计时器泄漏(未恢复 vi.useFakeTimers)。
    • pytest:数据库状态在测试之间泄漏。使用 @pytest.mark.usefixtures("db") 并通过事务回滚。检查 fixture 中是否修改了全局状态。
    • Go:使用 t.Parallel() 的测试共享包级状态。使用 t.Cleanup 执行清理。 使用 goleak 检查是否有 goroutine 泄漏。
  4. 谨慎重试:--retries 2(Playwright)或 --reruns 2(pytest-rerunfailures)只是临时补救,并非修复
覆盖率阈值

对于已设置覆盖率门槛的项目,将以下数值作为 CI 门禁参考;否则,不要为小改动引入门槛。合理的默认值:

指标阈值原因
行覆盖率80%能发现明显的覆盖缺口
分支覆盖率70%能发现未测试的条件

阈值是 CI 门禁,不是目标:绝不要仅仅为了提高数字而添加测试。可通过 vitest --coverage --coverage.thresholds.lines=80、pytest --cov --cov-fail-under=80,或 go test -coverprofile 加阈值脚本来强制执行。

最简 CI 示例(pytest + GitHub Actions):

- run: pip install pytest pytest-xdist pytest-cov
- run: pytest -n auto --cov=src --cov-fail-under=80 --tb=short

参考文件

  • references/language-patterns.md - JS/TS(Vitest、Jest)、Python(pytest)、Go(testing 标准库)和 Rust(cargo test)的语言专用测试模式。涵盖模拟、表驱动测试、异步测试、快照测试和框架专用惯用法。
  • references/e2e-accessibility.md - 使用 Playwright 进行 E2E 测试、视觉回归(截图对比、组件快照)、无障碍测试模式,以及浏览器测试的 CI 集成。

输出约定

完整约定请参阅 references/output-contract.md。

  • 技能名称: TESTING
  • 交付物类别: audits
  • 模式: 有条件。当调用以分析、审查、审计或改进现有仓库内容时,遵循 references/output-contract.md 中的报告篇幅和证据规则,并将交付物写入 docs/local/audits/testing/<YYYY-MM-DD>-<slug>.md。当调用以回答问题、讲解概念、构建新成果或生成内容时,可自由回复,不受该约定限制。
  • 严重程度级别: P0 | P1 | P2 | P3 | info(参见共享约定;仅用于审计/审查模式)。

相关技能

  • code-review — 在代码审查过程中评估测试的质量和正确性。此技能负责编写测试;code-review 负责评估测试是否真正覆盖了正确的内容。
  • security-audit — 负责安全专项测试(OWASP、渗透测试、凭据扫描)。此技能负责功能测试。
  • code-simplification — 审查冗长、过度抽象或由 AI 生成的测试代码,找出安全的精简方式。如果测试有效,但读起来像篇小说,请转交给 code-simplification。
  • ci-cd — 设计运行测试的流水线。此技能负责编写测试和配置测试运行器;ci-cd 负责测试流水线的整体结构。
  • databases — 涵盖数据库引擎测试和配置。此技能负责应用层面的数据库测试模式(事务、固定装置、测试数据)。
  • llm-app-development — 负责 AI/ML 模型评估、大模型输出评分和基准测试框架。此技能负责应用功能测试;llm-app-development 负责模型层面的评估。
  • kubernetes — 负责集群级混沌测试、韧性测试和基础设施层负载测试。此技能负责应用测试代码;kubernetes 负责集群级故障注入。

规则

AI 自检负责每项测试的防护措施(关注行为而非实现、不使用 sleep()、状态隔离、诊断不稳定测试、审查快照)。此外还需遵循:

  • 不要测试框架本身。 测试 React 是否渲染了一个 div,或 Express 是否将请求路由到某个处理程序,都是在测试他人的代码。要测试你自己的逻辑。
  • 避免测试膨胀。 不要针对同一行为编写近似重复的测试、复述实现的测试或过度模拟的测试。请参阅“适度原则”。

评论

支持 Markdown