mcpField
F
find-untested-sources-polyglot

find-untested-sources-polyglot

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

关于 find-untested-sources-polyglot

用于多语言代码库的静态测试缺口分析 skill,通过解析源文件与测试文件的符号和导入关系,列出可能缺少对应测试的源文件。它无需构建或运行测试,可为补测工作生成按声明数量排序的清单。

主要功能

  1. 01扫描九种编程语言
  2. 02基于 tree-sitter 解析源文件
  3. 03按导入和标识符匹配测试
  4. 04输出未测试文件及建议路径
  5. 05可筛选语言和清单数量

适用场景

  1. 01为新增测试选择目标文件
  2. 02测试生成前整理缺口清单
  3. 03检查新测试是否关联源文件
  4. 04快速筛查多语言仓库

安装

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

$npx skills add https://github.com/dotnet/skills --skill find-untested-sources-polyglot

安装后在 Claude Code 中输入/find-untested-sources-polyglot确认已加载。

附带文件scripts/find_untested_sources.py

详细介绍

查找未测试的源文件(多语言)

用途

覆盖率工具回答的是“哪些行已执行?”,它们要求构建成功且测试通过;在真实仓库中,这可能需要几分钟到几十分钟。 本技能要回答的问题不同,成本也低得多:

哪些源文件没有测试文件引用其中声明的任何符号?

这是 Agent 在编写新测试之前会提出的问题;只需用 tree-sitter 解析所有受支持的源文件,就能在几秒钟内静态回答,无需构建、解析依赖或编译。

这是 C# find-untested-sources 技能的多语言版本。输出架构有意保持兼容,因此两种工具都能使用相同的提示词模式。

适用场景

  • 仓库不完全由 C# 构成,或者你希望使用一种无需针对每种语言分别配置、可统一处理多种语言的工具。
  • 用户询问“我应该在哪里添加测试?”、“哪些文件没有测试?”、“查找未测试的代码”、“列出测试缺口”或“下一个要测试的文件是什么”。
  • 调用测试生成 Agent 之前,用于生成按优先级排序的工作清单。
  • 生成测试后,用于确认每个新测试文件都对应一个源文件。

不适用场景

  • 仅含 C# 的仓库——优先使用 find-untested-sources。它基于 Roslyn 的命名空间消歧能力,对于 Settings 或 Context 这类重复的短名称,明显优于本技能的标识符重叠匹配。
  • 行/分支覆盖率——使用语言原生覆盖率工具。
  • 现有测试是否有力?——使用 test-gap-analysis 或 assertion-quality。

输入

输入必填默认值说明
仓库根目录是—递归扫描的目录。
--lang LANG否所有语言限定语言(可重复指定)。可选项:python、typescript、tsx、javascript、go、java、rust、csharp、ruby。
--limit-untested N否0(不限制)将未测试列表截断至 N 项。
--include-tested否关闭在载荷中包含 tested_sources(内容较大)。
前置条件
  • Python 3.10+。
  • pip install tree-sitter-language-pack(一个自包含的 wheel,内置 300 多种语言的解析器,以及此处使用的高级 process() API)。无需本地构建,也无需按语言安装语法包。

用法

# From the skill folder
python scripts/find_untested_sources.py <repo-root>

# Restrict to a language
python scripts/find_untested_sources.py <repo-root> --lang python --lang typescript

# Truncate the report (top 20 by declared API surface)
python scripts/find_untested_sources.py <repo-root> --limit-untested 20 > pairing.json

# Iterate, highest-API-surface first
$report = Get-Content pairing.json | ConvertFrom-Json
$report.untested_sources | Select-Object -First 10 path, declaration_count, suggested_test_path

诊断信息写入 stderr;JSON 写入 stdout。

输出架构

{
  "repo_root": "<absolute path>",
  "summary": {
    "source_files": 3138,
    "test_files": 761,
    "tested_source_files": 1419,
    "untested_source_files": 1719,
    "orphan_test_files": 15,
    "languages": ["csharp"]
  },
  "untested_sources": [
    {
      "path": "src/Foo/Bar.cs",
      "language": "csharp",
      "declaration_count": 8,
      "declarations": ["Bar", "BarOptions", "IBar", "..."],
      "suggested_test_path": "src/Foo/BarTests.cs"
    }
  ],
  "orphan_tests": [
    { "path": "tests/SomeIntegrationTest.cs", "language": "csharp" }
  ]
}

传入 --include-tested 可额外输出 tested_sources(结构与 untested_sources 相同,但使用 covering_tests 数组而非建议路径)。默认省略,以减小供大模型使用的载荷。

工作原理

  1. 文件发现 — 递归遍历目录,并跳过常见的构建/供应商目录 (bin、obj、node_modules、target、dist、build、vendor、 __pycache__、.venv、.git 等)。跳过生成的文件(.d.ts、 .g.cs、.Designer.cs、_pb2.py、*.min.js、AssemblyInfo.cs 等)。

  2. 语言检测 — tree_sitter_language_pack.detect_language_from_path 将扩展名映射到受支持的语言之一。未知扩展名会被静默跳过。

  3. 测试与源代码分类 — 按语言使用路径启发式规则:

    语言测试规则
    Python路径包含 tests/ 或 test/;或文件名以 test_ 开头或以 _test.py 结尾;或文件名为 conftest.py。
    JS/TS/TSX路径包含 __tests__、tests、test、spec 或 e2e;或文件名包含 .test. 或 .spec.。
    Go文件名以 _test.go 结尾(Go 的标准约定)。
    Java路径包含 test 或 tests;或文件名以 Test.java / Tests.java 结尾。
    Rust路径包含 tests/ 或 benches/。
    C#路径包含 tests/;或项目路径段以 .Tests、.Test、.UnitTests、.IntegrationTests 结尾;或文件名以 Tests / Test 结尾。
    Ruby路径包含 spec/、test/;或文件名以 _spec.rb / _test.rb 结尾。
  4. 逐文件提取 — tree_sitter_language_pack.process(text, ProcessConfig(language=lang, structure=True, imports=True, symbols=True)) 返回:

    • structure — 顶层声明的项(函数、类、方法、 trait 等)及其名称。用于构成已声明符号集合。
    • imports — 原始导入语句(例如 from foo import bar、 import "pkg/util"、using System.IO;、use crate::foo::Bar;)。
    • symbols — 扁平的已声明名称列表,与 structure 合并(当 structure 为空时用作后备;两者都有内容时则扩大覆盖范围;声明数量可能超过仅解析结构时的数量)。
  5. 配对 — 对每个测试文件,合并以下结果:

    • 导入解析(按语言):
      • Python:from pkg.mod import x → pkg/mod.py 或 pkg/mod/__init__.py。
      • TS/JS:相对路径 ./foo / ../bar → 带 .ts / .tsx / .js / .jsx 扩展名以及 /index.* 的候选路径。
      • Go:"path/to/pkg" → 索引中最终路径段与 pkg.go 匹配的任意源文件。
      • Java:import a.b.C; → a/b/C.java。
      • Rust:use a::b::C; → b.rs 或 C.rs(尽力匹配,不含模块树)。
      • Ruby:require 'foo/bar' → foo/bar.rb。
      • C#:using 映射到命名空间,而不是文件;有意不执行任何操作 — 转而使用下面的标识符重叠方法。
    • 标识符重叠 — 在源代码索引中查找测试源代码中的每个类词标记, 匹配已声明名称(长度 ≥ 4,以减少噪声)。测试文件中出现其已声明名称的同语言源文件都会被配对。
  6. JSON 输出 — untested_sources 按声明数量降序排列, 使 API 面最广的缺口最先显示。

局限性

这是一种静态、仅解析的启发式方法。它有意牺牲少量准确性,以换取比全覆盖低几个数量级的成本。已知的不足:

  • 通过反射/DI 解析的类型:如果测试仅通过字符串名称或容器解析来引用,这些类型不会出现在标识符扫描结果中。
  • 特别是 C#:命名空间消歧是 C# 工具的优势;此多语言版本会有意跳过。如果你的代码库仅包含 .NET,建议使用基于 Roslyn 的 find-untested-sources。
  • 较短的标识符名称(< 4 个字符)会从重叠索引中剔除,以避免 id、db、Tag 等名称导致嘈杂的配对结果。
  • 跨语言测试(例如由 Python 测试驱动 Go 二进制文件)会被记录为孤立测试,因为配对规则要求语言相同。
  • Monorepo 路径别名(TS 路径映射、Java module-info)不会解析;如果不同子项目中的两个文件具有相同的末尾路径段,后缀匹配回退机制可能会选错源文件。

对于这些情况,请对 Agent 已完成初步筛选但尚未配对的候选项运行实际覆盖率测试。

Agent 应使用的输出

  • untested_sources[*].path — 选择下一个要测试的源文件(优先选择 declaration_count 最高的文件)。
  • untested_sources[*].suggested_test_path — 根据各语言的约定,为新测试文件提供可直接使用的目标路径。
  • (使用 --include-tested 时)tested_sources[*].covering_tests — 验证新编写的测试文件是否出现在预期源文件的列表中。
  • orphan_tests — 似乎未引用任何同语言源文件的测试;可用于筛查过时测试或仅用于集成测试的测试。

评论

支持 Markdown