find-untested-sources-polyglot について
複数言語のリポジトリを対象に、静的にテスト不足を調べるスキルです。ビルドやテスト実行をせずにソースとテストを解析し、未テストの可能性があるファイルを優先順に提示します。
主な機能
- 019種類の言語をスキャン
- 02tree-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
詳細
未テストのソースを探す(ポリグロット)
目的
カバレッジツールで分かるのは「どの行が実行されたか」です。これらのツールを使うにはビルドが成功し、テストが通る必要があり、実際のリポジトリでは数分から数十分かかります。 このスキルが答えるのは、それとは異なる、より低コストな問いです。
宣言されたシンボルのいずれもテストファイルから参照されていないソースファイルはどれですか?
エージェントが新しいテストを書く前に尋ねるのは、この問いです。tree-sitter で認識対象となるすべてのソースファイルを解析すれば、ビルドも、依存関係の解決も、コンパイルも不要で、数秒で答えられます。
これは、C# 用の find-untested-sources スキルに相当するポリグロット版です。出力スキーマは意図的に互換性を保っているため、どちらのツールにも同じプロンプトパターンを使用できます。
使用する場面
- リポジトリが C# 専用ではない場合、または言語ごとの設定をせずに複数の言語で一貫して使えるツールが必要な場合。
- ユーザーが「テストを追加すべき箇所はどこですか?」「テストがないファイルはどれですか?」「テストされていないコードを探して」「テストの不足箇所を一覧にして」「次にテストするファイルはどれですか?」と尋ねた場合。
- テスト生成エージェントを呼び出す前に、優先順位を付けた作業リストを作成する場合。
- テスト生成後に、新しい各テストファイルがソースファイルに対応していることを確認する場合。
使用しない場面
- 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(300 以上の言語用パーサーと、ここで使用する高水準のprocess()API をバンドルした、単体で完結する wheel)。ネイティブビルドも、言語ごとの文法のインストールも不要です。
使用方法
# 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 配列が含まれます)。LLM が扱うペイロードを小さく保つため、デフォルトでは省略されます。
動作の仕組み
-
ファイルの検出 — ディレクトリを再帰的に走査し、一般的なビルド/ベンダーディレクトリ(
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— 関数、クラス、メソッド、トレイトなど、トップレベルで宣言された項目とその名前です。宣言済みシンボルの集合として使用します。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のような名前でノイズの多い組み合わせが生じるのを避けるため、重複インデックスから除外されます。 - 言語をまたぐテスト(Go バイナリを操作する Python テストなど)は、同じ言語同士で組み合わせるのがルールのため、孤立テストとして記録されます。
- モノレポのパスエイリアス(TS のパスマッピング、Java の
module-info)は解決されません。別々のサブプロジェクトに末尾のパスセグメントが同じファイルがある場合、サフィックス一致のフォールバックによって、誤ったソースが選ばれる可能性があります。
このようなケースでは、エージェントがすでに選別した、組み合わせられていない候補に対して実際にカバレッジを実行してください。
エージェントが利用する出力
untested_sources[*].path— 次にテストするソースファイルを選択します(declaration_countが最も多いものを優先)。untested_sources[*].suggested_test_path— 言語ごとの規約に従った、新しいテストファイルの配置先です。- (
--include-testedを指定した場合)tested_sources[*].covering_tests— 新しく作成したテストファイルが、意図したソースの一覧に含まれていることを確認します。 orphan_tests— 同じ言語のソースファイルを参照していないように見えるテストです。古いテストや統合テストのみのテストを選別する際に役立ちます。