关于 test-tagging
用于分析现有测试套件并为测试方法应用标准化标签的技能,面向希望了解测试分布和关键路径覆盖的团队。它根据测试行为分类,并支持审计报告或按明确要求修改源码。
主要功能
- 01识别语言与测试框架
- 02按固定分类标记测试
- 03避免重复添加已有标签
- 04汇总标签分布
- 05支持审计或源码编辑
适用场景
- 01盘点测试套件中的正向与负向测试
- 02为未标记的测试添加标签
- 03检查关键路径测试覆盖
- 04生成标签分布报告
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dotnet/ skills --skill test-tagging安装后在 Claude Code 中输入/test-tagging确认已加载。
详细介绍
测试特征标签
分析任意受支持语言中现有的测试套件,并为每个测试方法应用一组标准化的特征标签,让团队了解测试分布情况(正向与负向、关键路径覆盖率、冒烟测试等)。
语言特定指南:阅读调用方提供或运行时列出的
test-analysis-extensions目录及其对应的语言文件。 此辅助工具仅供参考,不能调用。如果不存在, 请使用下方内置的框架表,并报告此限制。
适用场景
- 审核测试项目,了解各类测试的构成
- 为尚未标记的测试添加特征属性
- 生成测试套件中特征分布的汇总报告
- 检查关键路径是否有足够的覆盖
不适用场景
- 从头编写新测试(任意语言请使用
code-testing,MSTest 请使用writing-mstest-tests) - 运行或筛选测试(.NET 请使用
run-tests;其他语言请使用对应的原生运行器) - 在不同测试框架之间迁移
- 常规质量、异味、易失性或断言审核(请使用
test-anti-patterns或对应的分析技能) - 诊断 .NET 已执行行数、分支、Cobertura 的解读,或项目范围内的 CRAP 风险(请使用
coverage-analysis);原始覆盖率收集请使用run-tests(.NET)或其他语言的原生工具 - 分析指定方法、类或文件的 CRAP(请使用
crap-score) - 分析测试在生产逻辑损坏后仍能通过的行为缺口(请使用
test-gap-analysis)
输入
| 输入 | 必需 | 说明 |
|---|---|---|
| 测试项目或文件 | 否 | 测试项目、文件夹或特定测试文件的路径。省略时从当前工作区查找。 |
| 范围 | 否 | 根据动词推断:tag/apply 表示编辑,audit/classify/report 表示仅生成报告,只有同时请求时 both 才表示两者兼有。如有歧义,默认为 audit,以避免未经请求的编辑。标记为 report-only 的框架始终生成报告;标记为 convention-based 的框架只有在用户确认约定后才会编辑。 |
| 框架 | 否 | 自动检测。检测失败时可手动指定。 |
特征分类
请严格使用以下特征名称和值。不得使用表格以外的新特征值。
| 特征值 | 含义 | 判断依据 |
|---|---|---|
positive | 验证正常/有效条件下的预期行为 | 断言操作成功、输出有效、状态符合预期,且有效输入不会引发异常 |
negative | 验证对无效输入、错误或边缘情况的正确处理 | 断言会引发异常、返回错误代码、验证失败或拒绝错误输入 |
boundary | 测试限制、阈值、空/null/None/nil 输入、最小/最大值 | 使用 0、-1、int.MaxValue / sys.maxsize / Number.MAX_SAFE_INTEGER / math.MaxInt64 / i32::MAX、空字符串、null/None/nil/undefined、空集合或有效范围的边界 |
critical-path | 不得出错的核心工作流;一旦出错就会阻碍用户 | 测试关键公共 API 或面向用户功能的主要成功场景 |
smoke | 快速检查系统是否正常运行 | 执行迅速、无需复杂设置,验证基本连接(例如服务能够解析、端点返回 200) |
regression | 复现此前报告的特定错误 | 引用错误 ID、Issue 编号,或在名称或注释中描述修复内容 |
integration | 跨越进程、网络或持久化边界 | 使用真实数据库、HTTP 客户端、文件系统、外部服务或多组件设置 |
end-to-end | 覆盖整个应用栈的完整用户工作流 | 测试从入口到最终结果的完整场景,与仅跨越单一边界的 integration 测试不同 |
performance | 验证耗时、吞吐量或资源消耗 | 断言耗时、内存或分配量,或使用基准测试框架(BenchmarkDotNet、pytest-benchmark、benchmark.js、JMH、go test -bench、criterion.rs、XCTMetric、kotlinx-benchmark、Google Benchmark) |
security | 验证身份验证、授权、输入清理或机密信息处理 | 测试 SQL 注入、XSS、CSRF、未授权访问、令牌验证或权限检查 |
concurrency | 验证线程安全、并行处理或异步正确性 | 使用 Task.WhenAll / Parallel.ForEach / SemaphoreSlim(.NET);asyncio.gather / threading.Lock / multiprocessing(Python);Promise.all / worker 线程(JS/TS);CompletableFuture / ExecutorService / synchronized(Java);go func / sync.WaitGroup / sync.Mutex / chan(Go);Mutex / Thread.new(Ruby);tokio::spawn / Arc<Mutex<_>> / crossbeam(Rust);DispatchQueue / actor(Swift);coroutineScope / Mutex(Kotlin);Start-Job / RunspacePool(PowerShell);std::thread / std::mutex(C++);或复现竞态条件 |
resilience | 测试重试逻辑、超时、熔断器或优雅降级 | 断言在暂时性故障、网络中断或服务不可用时的行为(例如 Polly、tenacity、p-retry、resilience4j、hystrix、opossum、retry-go) |
destructive | 修改难以回滚的共享状态或外部状态 | 删除记录、销毁资源、修改全局配置——有助于决定 CI 隔离策略 |
configuration | 验证设置加载、默认值和环境行为 | 测试缺少配置键、无效值、环境变量回退或选项验证 |
flaky | 已知会间歇性失败(用于跟踪测试健康状况的元标签) | 标记团队已知不稳定的测试;用于隔离测试或确定稳定化工作的优先级 |
单个测试可以具有多个特征(例如同时具有 negative 和 boundary)。每个测试至少应标记为 positive 或 negative。
工作流
步骤 1:检测语言、框架和标签能力
在询问路径之前,先根据当前工作区确定请求的测试范围。技能上下文中的 Base directory 包含这些说明,而不是用户的代码仓库。声称无法访问仓库文件之前,务必先检查当前工作目录。如果提示中的相对路径不存在,请在工作区中搜索指定的项目或文件,并重试搜索到的确切路径。搜索成功即可证明目标存在;如果常规读取器随后报告该路径不存在,应将这种矛盾视为读取器路径规范化或传输故障,而不是向用户索要文件。仅在确认存在读取器可用性、传输或路径规范化故障后,且确认规范路径仍位于当前工作区内,才使用 shell 文本读取器(Unix 上的 sed/cat,PowerShell 上的 Get-Content)。遇到内容排除、权限/策略、工作区边界或未知读取故障时,立即停止。工作区搜索已找到可读取的目标后,绝不要再向用户索要路径或文件内容。
对于 auto-edit 框架,仅当确认故障属于工具可用性、传输或路径规范化问题时,补丁/编辑器调用失败才不构成停止条件。不要绕过过时上下文、并发更改、权限/策略或路径边界错误。使用 shell 作为后备方案之前,应在当前工作区内解析规范路径,重新读取文件,并使用锚定式转换;如果预期旧文本或精确匹配次数发生变化,则中止。随后重新打开完整文件、检查差异,并运行步骤 6 验证。用户要求应用属性时,不要把拟议属性说成已完成。
识别语言和框架。必要时阅读匹配的随附参考资料;使用提供的目录查找文件名,且只检查该已知目录。如果文件不存在,则根据以下内置规则判定能力:
auto-edit— 框架具有本技能可安全插入的规范标签语法(.NET[TestCategory]/[Trait]/[Category]/[Property]、pytest@pytest.mark.<name>、JUnit 5@Tag("...")、TestNGgroups = {"..."}、RSpec 元数据it "..." , :tag => true、Pester-Tag '...'、Kotest@Tags(...)、Swift Testing@Tag(.tagName)、Catch2[tag]、doctest* doctest::test_suite("tag")装饰器)。report-only— 框架没有规范且约定统一的标签属性;只能在 Markdown 表格中报告标签,不得编辑源代码(不使用构建标签约定的 Go 标准testing、没有统一 describe 前缀约定的 Jest/Vitest、没有项目特定 cfg 约定的 Rust、没有测试计划的 XCTest、没有测试名称前缀约定的 GoogleTest、没有 describe 前缀约定的 Mocha)。convention-based— 框架使用命名或文件约定进行标记(Go//go:build integration构建标签、*_integration_test.go之类的文件名后缀、GoogleTestINTEGRATION_*筛选器前缀)。只有在用户确认项目约定后,才应用规范编辑;否则按report-only处理。
在步骤 4 之前记录该能力。
分类之前,还要先锁定请求的模式。不要因为存在规范属性,就把审计改成源代码编辑;只有用户明确要求标记或应用时才编辑。
步骤 2:扫描现有特征
检查哪些测试已经带有特征属性。如果已加载扩展,请使用扩展;否则,以此内置表格为准:
| 框架 | 现有属性 | 示例 |
|---|---|---|
| MSTest | [TestCategory("...")] | [TestCategory("positive")] |
| xUnit | [Trait("Category", "...")] | [Trait("Category", "positive")] |
| NUnit | [Category("...")] | [Category("positive")] |
| TUnit | [Property("Category", "...")] | [Property("Category", "positive")] |
| JUnit 5 | @Tag("...") | @Tag("positive") |
| TestNG | @Test(groups = {"..."}) | @Test(groups = {"positive"}) |
| pytest | @pytest.mark.<name> | @pytest.mark.positive |
| RSpec | it 之后的元数据 | it "...", :positive do |
| Pester | -Tag '...' | It '...' -Tag 'positive' |
| Kotest | @Tags(...) | @Tags(Positive) |
| Swift Testing | @Tag(.<name>) | @Test(.tags(.positive)) |
| Catch2 | 名称中的 [tag] | TEST_CASE("...", "[positive]") |
| doctest | * doctest::test_suite("...") 装饰器 | TEST_CASE("..." *doctest::test_suite("positive")) |
记录哪些测试已经带有标签,以避免重复添加。
步骤 3:对每个测试方法分类
建立一份规范清单,确保每个发现的测试只出现一次。 在清单中记录测试标识符、行为分类和特征;源代码编辑、逐项测试报告、总数和分布计数都使用相同的行。不要另行手动统计分母。发布前,核对报告的总数与清单行数是否一致,并验证每一行是否都计入各项已显示的特征计数。
对于没有特征的每个测试方法,分析:
- 方法名——包含
Invalid、Fail、Error、Throw、Reject、BadInput、Null、None、Nil、Negative、raises_、_throws_、_returns_error的名称提示negative - 断言类型——
Assert.ThrowsException/Assert.Throws/Should().Throw()/pytest.raises/expect(fn).toThrow/assertThrows/assert.Error(t, err)/expect { ... }.to raise_error/#[should_panic]/XCTAssertThrowsError/Should -Throw/EXPECT_THROW提示negative - 输入值——
null/None/nil/undefined、""、0、-1、int.MaxValue/sys.maxsize/Number.MAX_SAFE_INTEGER/math.MaxInt64/i32::MAX、空集合提示boundary - 设置复杂度——设置最少且只有基本断言提示
smoke;外部依赖(文件/数据库/网络/环境)提示integration - 注释和名称——提及问题编号或“regression” / “bug” / “fix for #...”提示
regression - 计时断言——
Stopwatch、BenchmarkDotNet、耗时检查;pytest-benchmark 固定装置;benchmark.js;JMH@Benchmark;go test -bench;criterion.rs;XCTMetric;Google Benchmark;kotlinx-benchmark 提示performance - 功能核心性——针对主要公共 API 入口或关键用户工作流的测试提示
critical-path - 安全模式——验证身份验证、检查权限、清理输入、测试注入攻击、处理令牌/密钥提示
security - 并行/异步结构——各语言的并发原语(参见 Trait Taxonomy 表)提示
concurrency - 故障注入——模拟故障、测试重试、超时或断路器提示
resilience - 状态变更——删除外部记录、释放资源、修改共享/全局状态提示
destructive - 全栈流程——测试从入口到数据层再到最终响应,覆盖完整用户场景,提示
end-to-end - 配置/设置——加载配置、测试缺失键、验证选项、检查环境变量提示
configuration - 已知不稳定性——测试带有注释提及不稳定性的 skip / ignore 注解,或名称包含“flaky” / “intermittent”,提示
flaky - 默认——如果测试验证正常成功路径,则标记为
positive
如果无法确定应选 positive 还是 negative,请查看断言:如果断言成功,则选 positive;如果断言失败,则选 negative。
对于请求的分布或覆盖形态审计,使用可用的生产代码,将每个测试映射到其实际覆盖的具体结果,再进行总结。
指出重复的边界覆盖情况,以及测试清单是否涵盖了具名阈值的两侧和业务关键路径上可观察到的协作者结果。将这些内容作为简洁的分布观察,而不是新增特征值。不要进行变异分析、提出新测试建议,也不要扩展到由 test-gap-analysis 负责的行为缺口审计。
步骤 4:应用 trait 属性(或仅报告)
应用能力前,先确定模式:
- 审计模式(
audit、classify、report或意图不明确):输出逐项测试映射和摘要,不修改源代码,无论具备何种能力。 - 编辑模式(
tag、apply或明确要求both):继续执行下方的能力分支。
在编辑模式下,如果已确定的能力为 auto-edit,则为每个测试方法添加相应属性。将 trait 属性放在现有测试属性旁边。示例:
在单个测试方法/用例级别应用 trait。不要用类级别的单一分类替代方法级别的分类:不同方法通常会测试不同的正向、负向和边界行为。
MSTest:
[TestMethod]
[TestCategory("negative")]
[TestCategory("boundary")]
public void Parse_NullInput_ThrowsArgumentNullException() { ... }
xUnit:
[Fact]
[Trait("Category", "positive")]
[Trait("Category", "critical-path")]
public void CreateOrder_ValidItems_ReturnsConfirmation() { ... }
NUnit:
[Test]
[Category("regression")]
[Category("negative")]
public void Calculate_OverflowInput_ReturnsError() // Fix for #1234
{ ... }
pytest:
@pytest.mark.negative
@pytest.mark.boundary
def test_parse_none_input_raises_value_error():
...
JUnit 5:
@Test
@Tag("positive")
@Tag("critical-path")
void createOrder_validItems_returnsConfirmation() { ... }
TestNG:
@Test(groups = {"negative", "boundary"})
public void parse_nullInput_throwsIllegalArgumentException() { ... }
RSpec:
it "rejects null input", :negative, :boundary do
...
end
Pester:
It 'Rejects null input' -Tag 'negative','boundary' {
...
}
Kotest:
@Tags(Negative, Boundary)
class ParserSpec : StringSpec({
"rejects null input" { ... }
})
Swift Testing:
@Test(.tags(.negative, .boundary))
func parseNullInputThrows() throws { ... }
Catch2:
TEST_CASE("Parse null input throws", "[negative][boundary]") { ... }
在任何模式下,如果已确定的能力为 report-only(Go 标准 testing、没有约定的纯 Jest/Vitest、没有项目专属 cfg 的 Rust、纯 XCTest、纯 GoogleTest、纯 Mocha),则不要修改源文件。应改为输出每项测试及其建议标签的简明映射。仅当用户询问如何持久化或筛选这些标签时,才建议项目级约定;仅要求分析时,应报告后停止。
在编辑模式下,如果已确定的能力为 convention-based(例如 Go 的 //go:build integration、*_integration_test.go、GoogleTest 的 INTEGRATION_* 前缀),只有在用户已确认项目约定时,才输出规范编辑。否则按 report-only 处理。
步骤 5:生成 trait 摘要
添加标签后,生成摘要表。除非用户要求提供完整分类体系,否则仅包含计数非零的 trait;计数为零的行会掩盖测试套件的实际构成。对于规模较小、仅报告的测试套件,应将逐项测试映射与非零分布放在一起,不要扩展成仪表板。
## Trait Distribution
| Trait | Count | % of Total |
|---------------|-------|------------|
| positive | 50 | 64.1% |
| negative | 28 | 35.9% |
| boundary | 8 | 10.3% |
| critical-path | 12 | 15.4% |
| **Total tests** | **78** | -- |
Note: Percentages exceed 100% because tests can have multiple traits.
包含以下观察结果:
- 正向测试与负向测试的比例
- 关键公共 API 是否有关键路径测试
- 无法有把握地分类的测试(列出以供人工审核)
boundary 和所有其他专用 trait 都是附加标签。边界成功用例仍计为 positive;被拒绝的边界用例仍计为 negative。应用此规则后再计算正向/负向分布。
步骤 6:报告前验证编辑
对于每种 auto-edit 框架,运行范围最小的命令,以编译已编辑的属性并确认测试发现。即使用户只要求添加标签,也必须执行此步骤:语法上看似合理的属性并不代表编辑已完成。
| 框架 | 最低验证要求 |
|---|---|
| .NET | 运行 dotnet build <test-project>,然后通过 dotnet test <test-project> --list-tests --no-build 确认测试发现。除非用户要求,否则不要执行测试套件;将执行测试的操作交由 run-tests。 |
| pytest | 使用仓库配置的 pytest 命令收集已编辑的测试套件 |
| JUnit/TestNG | 通过仓库的 Maven/Gradle 测试任务编译测试 |
| 其他 auto-edit 框架 | 使用仓库中范围最小的编译或测试发现命令 |
如果编辑或应用补丁的过程不确定,应在验证前重新打开完整的已编辑文件,并将清单中的每一项与对应测试旁的实际属性逐一核对。不要仅凭不完整的差异或预期补丁报告成功。如果验证失败,应报告确切的命令和错误;对于未编译的编辑,绝不要发布成功的分布交接报告。
验证
- 每个测试方法至少有一个特征分类(至少为
positive或negative)——对于report-only框架,在报告中标注;对于auto-edit框架,将其作为属性 - 总数与逐项测试清单中的数量一致,展示的特征数量由该清单统计得出
- 未使用分类表以外的自创特征值
- 保留现有特征属性,未重复添加
- 已生成特征汇总表
- 对于
auto-edit框架,项目仍可构建/仍可发现测试,且未执行未请求的测试(dotnet build加列表模式/pytest --collect-only/mvn test-compile/go vet ./.../cargo check --tests/npm run test:list/等效命令) - 最终摘要列出成功的验证命令,并在有可用的发现命令时注明发现的测试数量
- 对于
report-only框架,未修改任何源文件 - 对于
convention-based框架,仅在确认项目约定后才应用编辑
常见陷阱
| 陷阱 | 解决方案 |
|---|---|
| 未阅读测试主体就猜测特征 | 始终阅读断言和设置代码,以便准确分类 |
只给测试标注 boundary,没有标注 positive/negative | 每个测试还应标注 positive 或 negative——boundary 是附加分类 |
| 检测到框架后使用了错误的属性语法 | 使用已加载扩展或内置表中对应的语法(不要在 xUnit 中使用 [TestCategory],也不要在 unittest 中使用 @pytest.mark.x) |
| 重复添加已有的分类属性 | 添加前,在步骤 2 中检查是否已有特征 |
过度使用 critical-path 标注 | 仅用于主要公共入口点上的测试,不要用于每个辅助函数 |
| 编辑 Go/纯 Jest/纯 Rust/纯 XCTest/纯 GoogleTest 源文件 | 这些默认属于 report-only——应改为生成 Markdown 表格。只有用户确认存在项目级约定(构建标签、文件后缀、describe 前缀、测试计划分组)时才编辑。 |
| 为基于约定的框架臆造标签前缀 | 采用约定前,先确认项目已有的约定——不要在 _integration_test.go、//go:build integration 或 IntegrationTest 前缀之间自行猜测 |
| 遗漏特定语言的并发/异步原语 | 如果有可用的已加载扩展,则使用它;否则使用特征分类表中的并发条目 |