mcpField
E
exp-test-maintainability

exp-test-maintainability

github.com/dotnet测试与质量5.8k37 次安装1 天前更新
代码仓库

关于 exp-test-maintainability

用于分析 .NET 测试代码可维护性问题并提出具体重构建议的技能,不会修改文件。它帮助开发者识别真实重复,同时兼顾测试清晰度与独立性。

主要功能

  1. 01查找重复构造与断言
  2. 02识别复制粘贴的测试方法
  3. 03检查初始化与测试基础设施
  4. 04要求模式至少出现三次
  5. 05提供重构示例及权衡

适用场景

  1. 01评估 .NET 测试项目中的重复
  2. 02寻找参数化测试候选项
  3. 03检查多个测试类的共享初始化

安装

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

$npx skills add https://github.com/dotnet/skills --skill exp-test-maintainability

安装后在 Claude Code 中输入/exp-test-maintainability确认已加载。

详细介绍

测试可维护性评估

分析 .NET 测试代码中的可维护性问题:重复的样板代码、复制粘贴的测试方法,以及测试方法和类之间的结构重复。生成一份报告,列出重构机会,并提供具体的重构前后建议。目标仅为分析——不要修改任何文件。

适用场景

  • 用户要求查找测试中的重复代码或样板代码
  • 用户想知道如何减少测试代码中的重复
  • 用户要求减少测试重复、提升测试可读性或精简测试样板代码
  • 用户要求找出测试套件中的重构机会
  • 用户想找出适合共享的初始化或清理逻辑
  • 用户问“我的测试中有哪些重复模式?”
  • 用户想集中管理测试数据、引入构建器或辅助方法

不适用场景

  • 用户想从头编写新测试(使用 writing-mstest-tests)
  • 用户想检测反模式或代码异味(使用 test-anti-patterns)
  • 用户想实际执行重构(直接协助他们;此技能仅用于分析)

输入

输入必填说明
测试代码是一个或多个待分析的测试文件,或测试项目目录
生产代码否被测代码,可用于了解哪些抽象可能有帮助
范围否指定在单个类内分析,还是跨多个类分析

工作流

第 1 步:收集测试代码

读取用户提供或引用的所有测试文件。如果用户指定目录或项目,请使用以下框架标记扫描所有测试文件:

框架测试类标记测试方法标记
MSTest[TestClass][TestMethod]、[DataTestMethod]
xUnit(无——基于约定)[Fact]、[Theory]
NUnit[TestFixture][Test]、[TestCase]、[TestCaseSource]
TUnit(无——基于约定)[Test]
第 2 步:识别可维护性问题

按以下类别扫描:

类别 1:重复构造对象

查找在 3 个或更多测试方法中使用相同或几乎相同参数构造的同一对象。

判断依据:

  • 多个测试中出现参数相同的 new ClassName(...)
  • 多个测试创建配置相似的同一“被测系统”
  • 重复创建并进行相同设置的模拟对象/伪对象/桩对象

潜在重构方案:

  • 提取工厂方法或测试辅助方法(例如 CreateSut()、CreateDefaultOrder())
  • 使用 [TestInitialize]/构造函数/[SetUp] 共享对象构造逻辑
  • 对具有多种变体的复杂对象引入构建器模式

示例——重构前:

[TestMethod]
public void Process_ValidOrder_Succeeds()
{
    var logger = new FakeLogger();
    var email = new FakeEmailService();
    var inventory = new FakeInventory(stock: 100);
    var processor = new OrderProcessor(logger, email, inventory);
    // ...
}

[TestMethod]
public void Process_EmptyItems_Fails()
{
    var logger = new FakeLogger();
    var email = new FakeEmailService();
    var inventory = new FakeInventory(stock: 100);
    var processor = new OrderProcessor(logger, email, inventory);
    // ...
}

重构后——提取工厂方法:

private static OrderProcessor CreateProcessor(int stock = 100)
{
    return new OrderProcessor(new FakeLogger(), new FakeEmailService(), new FakeInventory(stock));
}
类别 2:重复断言模式

查找在 3 个或更多测试方法中重复出现的同一组断言。

判断依据:

  • 多个测试对结果对象断言相同的一组属性
  • 重复出现先检查非空、再检查值的断言序列
  • 不同方法中重复出现相同的一组 Assert.AreEqual 调用

潜在重构方案:

  • 提取自定义断言辅助方法(例如 AssertValidOrder(order, expectedTotal, expectedStatus))
  • 使用框架专用的断言扩展
  • 引入 Verify 方法,检查一组标准属性
类别 3:复制粘贴式测试方法

查找方法体几乎相同、仅输入值或单个参数不同的测试方法。

判断指标:

  • 3 个或更多方法的结构相同,但字面值不同
  • 可以合并为 [DataRow]/[Theory]/[TestCase] 的方法
  • 测试名称遵循 Method_Input1_Result、Method_Input2_Result 之类的模式

潜在重构方式:

  • 将其转换为参数化测试,使用 [DataRow]/[InlineData]/[TestCase]
  • 对于复杂输入,使用 [DynamicData]/[MemberData]/[TestCaseSource]
  • 如果所有值都是编译时常量,优先使用带 DisplayName 的 [DataRow],而不是 [DynamicData]。将 [DynamicData] 留给计算得出的值或复杂值。
  • 为不直观的参数值添加 DisplayName。[DataRow("Gold", 100.0, 90.0)] 一目了然;[DataRow(3, 7, 42)] 则不然。
类别 4:重复的初始化/清理逻辑

查找在多个测试类中重复出现的初始化或清理代码。

判断指标:

  • 多个 [TestInitialize]/[SetUp] 方法的代码相似
  • 重复进行数据库填充、文件创建或 HTTP 客户端配置
  • 多个类中存在相同的 using/IDisposable 清理模式

潜在重构方式:

  • 提取共享测试基类或测试夹具
  • 使用组合方式搭配共享辅助类
  • 创建测试上下文工厂
类别 5:重复的测试基础设施

查找多个测试类中共有的结构模式。

判断指标:

  • 多个类中以相同方式配置了相同的模拟接口
  • 重复使用相似的 DelegatingHandler 模式来设置 HttpClient
  • 多个测试类中存在相同的日志记录/配置框架代码

潜在重构方式:

  • 提取共享测试夹具或辅助库
  • 创建可复用的伪实现
  • 引入测试框架类
步骤 3:应用校准规则

报告之前,根据以下规则筛选发现项:

  • 仅报告出现 3 次或更多的情况。 两处相似的设置不算样板代码,也可能是有意为之的清晰表达。
  • 不要标记简单构造函数。 new Calculator() 或 new List<int>() 不属于有意义的样板代码。也不要建议为 new User(1, "Alice") 使用构建器。
  • 尊重有意为之的冗长写法。 如果每个测试都能独立完成且易于理解,那么在每个测试中显式设置是合理的做法。可以提及,但不要将其标记为问题。
  • 区分结构相似与真正重复。 遵循 AAA(Arrange-Act-Assert)的测试自然会显得相似。只有实际代码(而不只是结构)重复时才标记。
  • 考虑重构的影响范围。 一个被 20 个测试共享的辅助方法会引入耦合。请指出这一权衡。
  • 如果测试套件已维护良好,请明确说明。 报告中只发现少量改进机会完全合理。肯定已有的优点。
步骤 4:报告发现项

按照以下结构呈现发现项:

  1. 摘要 — 按类别说明发现的模式数量。如果测试套件没有问题,开头就说明这一点。
  2. 按类别列出发现项 — 对于发现的每种模式:
    • 类别名称和描述
    • 位置:列出涉及的具体测试方法和文件
    • 重复代码模式(展示一个典型示例)
    • 建议的重构方式,并提供具体的重构前/后示例
    • 预计影响:可精简多少行代码/方法
  3. 重构优先级 — 按以下因素排列发现项:
    • 出现次数(次数越多,价值越高)
    • 重复代码的复杂度(复杂设置优先于简单构造)
    • 风险(优先进行低风险提取)
  4. 权衡 — 对于每项建议,请说明:
    • 提升了哪些可读性
    • 牺牲了哪些局部性/独立性
    • 根据出现次数,这么做是否值得

验证

  • 每条发现都包含具体的文件和方法位置
  • 每条发现都展示实际重复的代码,而不只是描述
  • 每条建议都包含具体的前后示例
  • 发现已按出现次数至少 3 次的阈值筛选
  • 不标记简单构造函数
  • 每条建议都说明了权衡
  • 如果测试全部通过,报告应在开头说明

常见问题

问题解决方案
将 AAA 结构标记为重复代码Arrange-Act-Assert 模式不是样板代码——只有实际代码重复时才标记
仅出现 2 次就建议提取等到出现 3 次或更多再建议提取
什么都建议用基类实现优先使用组合(辅助函数、工厂),而非继承
忽视可读性成本每次提取都会增加间接层——应说明这种权衡
将简单的 new X() 标记为样板代码仅标记包含多个参数或配置的复杂构造
为追求 DRY 而牺牲测试隔离性通过辅助函数共享可变状态的测试会产生耦合——应对此发出警告

评论

支持 Markdown