关于 find-untested-sources-polyglot
用于多语言代码库的静态测试缺口分析 skill,通过解析源文件与测试文件的符号和导入关系,列出可能缺少对应测试的源文件。它无需构建或运行测试,可为补测工作生成按声明数量排序的清单。
主要功能
- 01扫描九种编程语言
- 02基于 tree-sitter 解析源文件
- 03按导入和标识符匹配测试
- 04输出未测试文件及建议路径
- 05可筛选语言和清单数量
适用场景
- 01为新增测试选择目标文件
- 02测试生成前整理缺口清单
- 03检查新测试是否关联源文件
- 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 数组而非建议路径)。默认省略,以减小供大模型使用的载荷。
工作原理
-
文件发现 — 递归遍历目录,并跳过常见的构建/供应商目录 (
bin、obj、node_modules、target、dist、build、vendor、__pycache__、.venv、.git等)。跳过生成的文件(.d.ts、.g.cs、.Designer.cs、_pb2.py、*.min.js、AssemblyInfo.cs等)。 -
语言检测 —
tree_sitter_language_pack.detect_language_from_path将扩展名映射到受支持的语言之一。未知扩展名会被静默跳过。 -
测试与源代码分类 — 按语言使用路径启发式规则:
语言 测试规则 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结尾。 -
逐文件提取 —
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为空时用作后备;两者都有内容时则扩大覆盖范围;声明数量可能超过仅解析结构时的数量)。
-
配对 — 对每个测试文件,合并以下结果:
- 导入解析(按语言):
- 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映射到命名空间,而不是文件;有意不执行任何操作 — 转而使用下面的标识符重叠方法。
- Python:
- 标识符重叠 — 在源代码索引中查找测试源代码中的每个类词标记, 匹配已声明名称(长度 ≥ 4,以减少噪声)。测试文件中出现其已声明名称的同语言源文件都会被配对。
- 导入解析(按语言):
-
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— 似乎未引用任何同语言源文件的测试;可用于筛查过时测试或仅用于集成测试的测试。