mcpField
T
testing

testing

github.com/krzysztofsurdy测试与质量3651 次安装3 天前更新
代码仓库

关于 testing

面向开发者与团队的跨技术栈测试参考,涵盖测试设计、测试层级、TDD、测试替身及维护可靠测试套件的方法。

主要功能

  1. 01单元、集成与端到端测试指南
  2. 02Arrange-Act-Assert 测试设计
  3. 03TDD 红绿重构循环
  4. 04测试替身与边界使用原则
  5. 05不稳定测试及反模式指南

适用场景

  1. 01为新功能选择测试层级
  2. 02排查不稳定或脆弱的测试套件
  3. 03审查测试的清晰度与可维护性
  4. 04用 TDD 探索设计决策

安装

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

$npx skills add https://github.com/krzysztofsurdy/code-virtuoso --skill testing

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

包含的文件

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

references/
tdd-schools.md
test-doubles.md
testing-strategies.md

详细介绍

测试

一种严谨的方法,用于验证软件是否按预期运行、在变更时保持稳定,并向未来的开发者传达设计意图。好的测试既是持续更新的文档,也是重构的安全网和设计反馈机制。

本技能涵盖通用测试概念,无论使用哪种语言、框架或工具,都适用。

适用场景

  • 为新项目或新功能设计测试策略
  • 判断一段代码需要何种级别的测试(单元测试、集成测试、端到端测试)
  • 评估现有测试是否有价值,还是反而增加了负担
  • 运用 TDD 推动设计决策
  • 调试不稳定或脆弱的测试套件
  • 审查测试代码的质量和可维护性

测试金字塔

测试金字塔描述了三个层级测试的理想分布:底层测试较多,顶层测试较少。

        /  E2E  \           Few, slow, expensive
       /----------\
      / Integration \       Moderate number, moderate speed
     /----------------\
    /    Unit Tests     \   Many, fast, cheap
   /____________________\
单元测试(底层)
  • 隔离测试单一行为单元(函数、方法或小型类)
  • 不涉及 I/O、数据库、网络或文件系统
  • 执行时间以毫秒计
  • 应占测试套件的大多数(约 70%)
  • 快速反馈循环可支持快速迭代
集成测试(中层)
  • 测试多个单元如何协作,或代码如何与外部系统交互
  • 可能会涉及真实的数据库、消息队列或 HTTP 端点
  • 执行时间以秒计
  • 验证组件之间的连接、配置和契约是否正常工作
  • 约占测试套件的 20%
端到端测试(顶层)
  • 测试贯穿整个系统的完整用户旅程
  • 像用户一样与应用程序交互
  • 速度最慢、最脆弱、维护成本最高
  • 仅用于关键业务路径
  • 约占测试套件的 10%
冰淇淋筒反模式

倒置的金字塔:端到端测试很多,单元测试很少。表现包括:

  • 测试套件需要数小时才能运行
  • 测试常因 UI 变更或时序问题而失败
  • 开发者不再在本地运行测试
  • 反馈循环太慢,无法支持持续交付

解决方法: 明确每个端到端测试实际验证的内容,并将验证下移到尽可能低的层级。大多数业务逻辑都可以通过单元测试来验证。

测试设计原则

准备-执行-断言(AAA)

每个测试都应遵循三个明确阶段:

  1. 准备(Arrange) — 设置前置条件和输入
  2. 执行(Act) — 执行待测行为
  3. 断言(Assert) — 验证预期结果

清楚地区分各个阶段。如果准备阶段在测试中占主导,可提取一个构建器或工厂。如果执行阶段需要多个步骤,你可能一次测试了太多内容。

每个概念一项断言

一个测试应验证一个逻辑概念。这并不意味着只能调用一次 assert——对单个结果断言多个属性没有问题。关键在于,测试失败时应只有一个明确的原因。

// Good: one concept — "completed order has correct totals"
assert order.subtotal == 100
assert order.tax == 21
assert order.total == 121

// Bad: two unrelated concepts in one test
assert order.total == 121
assert emailService.wasCalled()
测试命名

测试名称应描述行为,而非实现。好的测试名称应回答:“测试的是什么场景,预期结果是什么?”

适用于各种语言的命名模式:

  • should_return_zero_when_cart_is_empty
  • rejects_negative_quantities
  • applies_discount_for_premium_customers

避免使用 testCalculate、test1 或 testGetterSetter 这类名称。

测试独立性与隔离性

每个测试都必须完全独立于其他测试:

  • 测试之间不共享可变状态
  • 不要求按特定顺序执行
  • 每个测试自行设置前置条件并清理
  • 单个测试失败不应引发其他测试连锁失败
确定性测试

无论以下情况如何变化,测试每次运行都必须产生相同结果:

  • 当前时间或日期
  • 测试执行顺序
  • 运行测试的机器
  • 网络是否可用
  • 是否有其他测试并行运行

非确定性测试(不稳定测试)会摧毁对测试套件的信任,其危害甚至大于没有测试。

FIRST 原则
原则含义
快速测试应在几秒内运行完成,而不是几分钟。运行缓慢的测试不会被执行。
独立测试之间互不依赖,任何测试都不依赖其他测试的输出。
可重复在任何环境中都能得到相同结果——本地、CI、预发布环境皆如此。
自验证测试通过或失败都不需要人工解读。
及时在恰当的时机编写——最好在生产代码之前或与其同步编写。

测试驱动开发(TDD)

TDD 是一种设计规范:先编写测试,再编写生产代码,并遵循紧密的反馈循环。

Red-Green-Refactor 循环
  1. Red(红) — 编写一个描述预期行为的失败测试
  2. Green(绿) — 编写能让测试通过的最简单生产代码
  3. Refactor(重构) — 在保持所有测试通过的同时改进代码结构

规则:

  • 没有失败测试时,绝不编写生产代码
  • 只编写足以让测试失败的测试(编译失败也算)
  • 只编写足以让当前失败测试通过的生产代码
TDD 的两大学派
方面Chicago(经典派)London(Mock 派)
验证方式基于状态基于交互
方向由内向外由外向内
协作者真实对象Mock/Stub
优势测试不易受重构影响推动接口设计
风险深层对象图的设置复杂测试与实现耦合

有关详细对比和指导,请参阅 TDD 学派参考。

TDD 最适用的场景
  • 规则和边界情况明确的业务逻辑
  • 算法设计
  • API 契约定义
  • 复现并修复 Bug(先编写失败测试)
TDD 可能不适用的场景
  • 探索性原型开发(了解大致结构后再编写测试)
  • UI 布局和样式
  • 一次性脚本

测试替身

测试替身会在测试期间替代真实依赖。每种类型都有不同的用途。

替身用途是否验证?
Dummy填充参数列表,从不实际使用。否
Stub为方法调用提供预设响应。否
Spy记录交互,以便之后断言。是(事后)
Mock预先设置期望;调用不符合预期时会失败。是(内联)
Fake简化的可运行实现(例如内存中的仓库)。否

有关各类替身的适用场景,请参阅 测试替身参考。

核心原则:在边界处使用 Mock

在架构边界(端口、外部服务)使用测试替身,而不是在内部协作者之间使用。Mock 内部类会使测试与实现细节耦合,让重构变得痛苦。

应测试什么/不应测试什么

高价值——始终测试
  • 业务规则和领域逻辑
  • 边界情况、边界条件、错误路径
  • 状态转换和工作流
  • 输入验证和清理
  • 安全关键路径(身份验证、授权)
  • 数据转换和计算
低价值——通常跳过
  • 没有逻辑的简单 getter/setter
  • 框架生成的代码(ORM 映射、路由配置)
  • 第三方库内部实现(测试集成,而非其代码)
  • 私有方法(通过公共 API 测试)
  • 日志和遥测(除非对业务至关重要)
测试实现还是行为

测试行为,而不是实现。 好的测试描述系统做什么,而不是系统内部如何实现。

测试实现的迹象:

  • 重构后行为未变,测试却失败
  • 测试断言内部方法调用的顺序
  • 测试验证私有状态,而不是公共输出
  • 重命名内部类会导致无关功能的测试失败

测试行为的迹象:

  • 测试描述了对用户有意义的场景
  • 内部重构后测试仍然通过
  • 测试断言通过公共 API 可见的输出、副作用或状态变化

按层划分的测试策略

不同的架构层需要采用不同的测试方法。详细指南请参阅 测试策略参考。

层主要测试类型关键技术
领域/业务逻辑单元测试基于状态的验证,不进行 I/O
应用服务单元测试 + 集成测试为基础设施端口使用测试替身
数据访问集成测试使用真实数据库(测试容器、内存数据库)
API 端点集成测试 + 契约测试验证请求/响应
UI 组件组件测试模拟交互
完整系统端到端测试(有选择地进行)仅测试关键路径

常见反模式

反模式症状修正方法
脆弱的测试即使行为未变,每次重构都会导致测试失败通过公共 API 测试行为,而非内部结构
测试实现细节断言方法调用顺序、私有状态或内部连接方式断言输出和可观察的副作用
测试套件运行缓慢测试套件需要 10+ 分钟;开发者会跳过测试将测试下沉到测试金字塔更低层;对 I/O 使用测试替身
不稳定的测试代码未变,测试却随机通过或失败移除时间依赖、共享状态和顺序假设
过度使用模拟对象模拟对象的设置代码比实际测试逻辑还多;测试难以阅读尽可能使用真实协作者;仅在边界处使用模拟对象
测试数据耦合测试共享固定数据,共享数据变更时测试就会失败每个测试自行创建数据;使用构建器/工厂
缺少错误路径测试只测试成功路径;问题在生产环境中才被发现明确测试错误情况、边缘情况和边界条件
注释掉的测试禁用失败的测试,而不是修复或删除修复测试;如果行为是有意变更的,则删除测试
庞大的测试方法测试超过 50 行,包含多个执行和断言步骤拆分为专注的测试;将准备工作提取到辅助方法中
没有断言测试执行了代码,却从未进行断言每个测试都必须至少包含一个有意义的断言

质量检查清单

编写或审查测试时,请使用此清单:

  • 聚焦行为:测试描述系统做什么,而非如何做
  • 相互独立:测试不依赖其他测试的执行或状态
  • 结果确定:在任何机器上,每次运行的结果都相同
  • 运行快速:单元测试在毫秒内完成,完整测试套件在 5 分钟内完成
  • 易于阅读:新团队成员无需阅读实现代码就能理解测试
  • 安排清晰:采用 AAA 结构,各阶段划分明确
  • 命名清楚:测试名称说明场景和预期结果
  • 覆盖错误路径:不只测试成功路径,还要测试边缘情况和失败情况
  • 准备工作精简:不使用不必要的依赖项或固定数据;必要时使用构建器/工厂
  • 没有不稳定因素:不依赖时间、顺序或环境的测试
  • 层级适当:在能提供信心的最低测试金字塔层级进行测试
  • 在边界处使用测试替身:在架构端口处使用模拟对象/桩对象,而非在内部类中使用

评论

支持 Markdown