find-untested-sources
Skill1.1k starsChanged 23 days agoMITView on GitHub
MANDATORY for static source-to-test pairing: find or list source files/modules without corresponding tests, or suggest test locations from repository structure. Invoke even for a tiny package; do not substitute manual globbing. Uses Roslyn for C#/.NET and tree-sitter for Python, TS/JS, Go, Java, Rust, Ruby, Kotlin, Swift, PowerShell, and C++. DO NOT USE FOR: real line/branch/Cobertura data, coverage-backed test priorities, CRAP risk, or grading existing tests.
- Installs packages
We split this skill into the rules it gives an agent
7 rules. About 0 are common, 6 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 45
Use the narrowest repository or package root named by the caller.
- Distinctive: line 54
Treat analyzer dependencies as environment prerequisites: do not install packages, try the wrong engine, build the repository, or fall back …
- Distinctive: line 59
When the caller named a subdirectory, prefix analyzer-relative paths with that subdirectory so reported paths are workspace-relative.
- Distinctive: line 71
Before invoking a test-generation agent, to produce a source-pairing worklist.
- Distinctive: line 72
After generating tests, to verify each new test file pairs to a source file.
- Very specific: line 90
No internet access required beyond the initial NuGet restore of Microsoft.CodeAnalysis.CSharp on first run.
- Distinctive: line 277
Always label the final result as a static pairing heuristic, not evidence of line or branch coverage.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Find Untested Sources
Purpose
Coverage tools answer "which lines were executed?" — they require a green build and a passing test run, which is minutes-to-tens-of-minutes on a real repo. The question this skill answers is different and much cheaper:
Which source files have no test file referencing any of their declared types/symbols?
That's the question an agent asks before writing a new test — and it can be answered statically in a few seconds by parsing source files, with no build, no dependency resolution, and no compilation. The output is a deterministic test-pairing map that lets the agent pick the next file to test without reading the entire codebase first.
Two engines — pick one
This skill ships two interchangeable analyzers with a compatible JSON contract:
| Engine | Script | Use when |
| Roslyn (C#) | scripts/Find-UntestedSources.cs | The repo is .NET-only. Parses every .cs file with the Roslyn syntax API and does strict namespace disambiguation, so it is materially more accurate on duplicated short names like Settings or Context. |
| tree-sitter (polyglot) | scripts/find_untested_sources.py | The repo is not exclusively C#, or you want one tool across C#, Python, TypeScript/JavaScript, Go, Java, Rust, Ruby, Kotlin, Swift, PowerShell, and C++. |
For a .NET-only repository, prefer the Roslyn engine — its namespace-aware pairing beats the polyglot engine's identifier overlap.
Required workflow
- Use the narrowest repository or package root named by the caller. Do not scan a parent workspace when the request identifies a subdirectory.
- Execute the appropriate analyzer once. Do not replace analyzer execution with
manual globbing, filename matching, or visual inspection.
For polyglot analysis, pass
--include-testedwhen the answer must distinguish paired sources from unpaired sources. "Static pairing only" prohibits compiling the target repository and running its tests; it does not prohibit launching this skill's parse-only analyzer. State that distinction briefly when the caller also says "do not build." Treat analyzer dependencies as environment prerequisites: do not install packages, try the wrong engine, build the repository, or fall back to a manual scan when an analyzer invocation fails. Report the prerequisite failure instead. - Base the result on the analyzer's JSON. Preserve its paired/unpaired classification and suggested relative path; do not guess a different path.
- When the caller named a subdirectory, prefix analyzer-relative paths with that subdirectory so reported paths are workspace-relative.
- Report the requested result plus the static-pairing coverage caveat. Do not append build, package-install, test-run, or coverage commands. When paired sources exist, name their covering test files so the unpaired classification is auditable.
When to Use
- User asks "where should I add tests based on source pairing?", "which files have no tests?", "find unpaired source files", or "give me a static test gap list".
- Before invoking a test-generation agent, to produce a source-pairing worklist.
- After generating tests, to verify each new test file pairs to a source file.
- To enumerate "weakly paired" source files (only one referring test) for follow-up depth checks.
When Not to Use
- Line/branch coverage — use
coverage-analysis. - Priorities derived from real coverage data — use
coverage-analysis. - CRAP-score / risk hotspots — use
coverage-analysis. - Are existing tests strong? — use
test-gap-analysis(mutation reasoning) orassertion-quality.
Roslyn engine (C#)
Prerequisites
- .NET SDK that supports file-based apps (
dotnet run script.cs). Pinned in the repo'sglobal.json(SDK 11 preview or later). - No internet access required beyond the initial NuGet restore of
Microsoft.CodeAnalysis.CSharpon first run.
Usage
# From the skill folder
dotnet run scripts/Find-UntestedSources.cs -- <repo-root> [--top N]
# Save the report
dotnet run scripts/Find-UntestedSources.cs -- <repo-root> > pairing.json
# Iterate the untested list, highest-API-surface first
$report = Get-Content pairing.json | ConvertFrom-Json
$report.untested | Select-Object -First 10 source, decl_count, suggested_test_pathDiagnostics go to stderr; JSON goes to stdout.
Output schema
{
"repo": "<absolute path>",
"elapsed_ms": 8883,
"counts": {
"source_files": 3036,
"test_files": 867,
"untested_files": 1852,
"paired_files": 1184
},
"untested": [
{
"source": "src/Foo/Bar.cs",
"decl_count": 8, // # of type declarations in the file
"suggested_test_path": // mirror of source under a discovered test project
"tests/Foo.Tests/Bar/BarTests.cs"
}
],
"source_to_tests": {
"src/Foo/Baz.cs": [
"tests/Foo.Tests/BazTests.cs",
"tests/Foo.IntegrationTests/Scenarios/BazScenarios.cs"
]
}
}How it works
- File discovery — recursive walk pruning
bin/,obj/,node_modules/,.git/,.vs/,packages/, and any dotted subdir. Skips generated files (.g.cs,.Designer.cs,.AssemblyInfo.cs). - Test vs source classification — walks up to the nearest
.csprojand marks it a test project if the project name ends in.Tests,.Test,.UnitTests,.IntegrationTests,.E2E,.EndToEnd,.Spec,.Specs, or the content referencesMicrosoft.NET.Test.Sdk,MSTest.Sdk,Microsoft.Testing.Platform,xunit,NUnit,TUnit, or<IsTestProject>true</IsTestProject>. - Source index (parallel) — parse each source file with
CSharpSyntaxTree.ParseText(syntax only, no compilation); record everyBaseTypeDeclarationSyntax/DelegateDeclarationSyntaxas(ShortName, EnclosingNamespace, FilePath). - Test scan (parallel) — parse each test file, collect
usingdirectives + enclosing namespace, walk everyIdentifierToken, look it up in the short-name index, and disambiguate strictly: an identifier is attributed only if the declaration's namespace matches one of the test file'susingdirectives, the enclosing namespace, or a prefix of them. This avoids noise where common names likeSettingsorContextmatch every project. - Pairing & suggestion — invert into
source → [tests]. Build a production-to-test project map from<ProjectReference>entries; for each untested source, mirror its in-project relative path under the referencing test project to suggest a path. - JSON emit — ordered by declaration count desc, then alphabetical.
Polyglot engine (tree-sitter)
Prerequisites
- Python 3.10+.
pip install tree-sitter-language-pack(single self-contained wheel that bundles parsers for 300+ languages and the high-levelprocess()API). No native build, no per-language grammar install.
Usage
# From the skill folder
python scripts/find_untested_sources.py <repo-root>
# Restrict to a language (repeatable)
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_pathPass --include-tested to additionally emit tested_sources (omitted by
default to keep the payload small for LLM consumption). Diagnostics go to
stderr; JSON goes to stdout.
Output schema
{
"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" }
]
}How it works
- File discovery — recursive walk pruning common build/vendor dirs (
bin,obj,node_modules,target,dist,build,vendor,__pycache__,.venv,.git, …) and generated files (.d.ts,.g.cs,.Designer.cs,_pb2.py,*.min.js,AssemblyInfo.cs, …). - Language detection —
detect_language_from_pathmaps the extension to a supported language; unknown extensions are skipped. Test-vs-source classification — per-language path heuristics:
Language Test rule Python path contains tests//test/; or filename starts withtest_or ends_test.py; orconftest.py.JS/TS/TSX path contains __tests__,tests,test,spec,e2e; or filename contains.test./.spec..Go filename ends _test.go.Java path contains test/tests; or filename endsTest.java/Tests.java.Rust path contains tests//benches/.C# path contains tests/; or project segment ends.Tests/.Test/.UnitTests/.IntegrationTests; or filename endsTests/Test.Ruby path contains spec//test/; or filename ends_spec.rb/_test.rb.Kotlin path contains test//tests//spec/; or filename endsTest.kt/Tests.kt/Spec.kt.Swift path contains test//tests//uitests//integrationtests/(case-insensitive); or filename endsTest.swift/Tests.swift.PowerShell path contains test//tests//pester/; or filename ends.Tests.ps1/.Test.ps1.C++ path contains test//tests//testing/; or filename startstest_or ends_test.cpp/_tests.cpp.- Per-file extraction —
process(text, ProcessConfig(structure, imports, symbols))returns declared items, raw import statements, and a flat declared -name list. - Pairing — for each test file, union import resolution (per language,
e.g. Python
from pkg.mod import x→pkg/mod.py; Javaimport a.b.C;→a/b/C.java; C#usingis namespace-not-file, so a no-op) with identifier overlap (word-like tokens, length ≥ 4, matched against declared names). - JSON emit —
untested_sourcesordered by declaration count descending.
Limitations (be honest with the agent)
Both engines are static, parse-only heuristics that trade a little accuracy for orders-of-magnitude lower cost than coverage. Known gaps:
- Reflection / DI-resolved types referenced only via a string name or container resolution won't be detected — the type's short name never appears in the test source.
- Extension methods invoked as instance methods (C#): the declaring static class is not named, so its file is not credited.
var, target-typednew(), pattern matching lose the type token; the file-level union usually still catches it through other references.- Short identifier names (polyglot, < 4 chars) are dropped to avoid noisy
pairings on names like
id,db,Tag. - Monorepo path aliases (TS path mapping, Java module-info) are not resolved; a suffix-match fallback may pick the wrong source if two files share a trailing path segment.
For these cases, run actual coverage (coverage-analysis) on the unpaired
candidates the agent has already triaged.
Always label the final result as a static pairing heuristic, not evidence of line or branch coverage. Include that caveat even when every requested source file has an obvious matching or missing test.
Outputs the agent should consume
untested[*].source/untested_sources[*].path— pick the next source file to test (highest declaration count first).*.suggested_test_path— drop-in target for the new test file; the Roslyn engine honors the test project that already<ProjectReference>s the source's project, sodotnet sln addis not needed. The polyglot engine may suggest a co-located test when no test root is discoverable. When a source sibling is already paired, its test directory is the established convention and must be reused for the missing sibling rather than falling back to source co-location.source_to_tests(Roslyn) /--include-testedtested_sources(polyglot) — verify a newly written test file lands in the list for the intended source.orphan_tests(polyglot) — tests that don't reference any same-language source file; useful for triaging stale or integration-only tests.
Trending
Quiet since September.about the whole repository
Who's talking about microsoft/testfx
- Stack Overflow“App.config not being loaded in .Net Core MSTests project”Stack Overflow · Feb 24, 2019 · 35 votes
- Stack Overflow“Visual Studio Test Explorer doesn't run tests after changes”Stack Overflow · Dec 21, 2022 · 31 votes
- Stack Overflow“DataTestMethod vs TestMethod”Stack Overflow · Dec 3, 2019 · 19 votes
- Stack Overflow“ConfigurationManager.AppSettings is always empty”Stack Overflow · May 5, 2020 · 17 votes
- dotnet/sdk“[Proposal]: Add Microsoft.NET.Sdk.Testing”GitHub · Jun 7, 2025 · 10 reactions
The biggest 5 of 68 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 68
Talked about on GitHub and Stack Overflow (68 times).
On GitHub · 41
- Jun 7, 2025[Proposal]: Add Microsoft.NET.Sdk.Testing — dotnet/sdksays it tested it10 reactions · 15 comments
- Feb 4, 2025Dependabot job fails due to attempt to update packages referenced via an MSBuild SDK — dependabot/dependabot-core9 reactions · 13 comments
- Feb 22, 2024Microsoft Testing Platform for xUnit — xunit/visualstudio.xunitsays it tested it6 reactions · 19 comments
- Dec 3, 2018DataTestMethod vs TestMethod — microsoft/testfx-docs6 reactions · 4 comments
- Feb 19, 2024Microsoft Testing Platform for NUnit — nunit/nunit3-vs-adaptersays it tested it5 reactions · 1 comment
36 more on GitHub
- Dec 19, 2023Arcade should opt into `BuildWithNetFrameworkHostedCompiler` by default — dotnet/arcade5 reactions · 18 comments
- Nov 24, 2025Native support for Microsoft.Testing.Platform? — danielpalme/ReportGeneratorsays it tested it4 reactions · 3 comments
- Oct 31, 2025Selecting test runner without global.json — dotnet/sdk3 reactions · 2 comments
- Jul 15, 2025[Analyzer Proposal]: Don't use `stringBuilder.Append(new string(c, repeatCount))` — dotnet/runtime3 reactions · 3 comments
- Jul 13, 2025Allow `dotnet test --list-tests json` — dotnet/sdk3 reactions · 7 comments
- Jul 9, 2025Improve output of `dotnet test` with MTP — dotnet/sdk3 reactions · 6 comments
- Jan 30, 2024Support for the new test runner — nunit/nunit3 reactions · 3 comments
- Oct 17, 2017[ExcludeFromCodeCoverageAttribute] should be applicable to assemblies — dotnet/runtime3 reactions · 12 comments
- Sep 2, 2026chore(deps): update mstest to 4.4.0 — thomhurst/TUnit2 reactions · 3 comments
- Dec 17, 2025[MTP] Cannot switch off terminal logger when building during dotnet test — dotnet/sdk2 reactions
- Oct 8, 2025Bump MSTest.TestFramework from 3.11.0 to 4.0.0 — files-community/Files2 reactions · 1 comment
- May 27, 2025Dependabot Ignores Configuration File Settings in latest Version — dependabot/dependabot-core2 reactions · 7 comments
- May 20, 2025Add `-vs` and `-vscode` to build scripts — dotnet/arcade2 reactions · 8 comments
- Mar 14, 2025Add template for dotnet.config to dotnet new — dotnet/sdk2 reactions · 2 comments
- Jan 16, 2024An Allure adapter for MSTest — allure-framework/allure-csharp2 reactions · 2 comments
- Apr 10, 2018Added docs for dynamic data display name — microsoft/testfx-docs2 reactions · 1 comment
- Sep 3, 2026chore(deps): update microsoft.testing — thomhurst/TUnit1 reaction · 3 comments
- Mar 16, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/FAnsiSql1 reaction
- Mar 16, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/libarchive.net1 reaction
- Mar 16, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/RDMP1 reaction
- Mar 16, 2026sdk(deps): Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/CsvHandler1 reaction · 1 comment
- Mar 16, 2026chore(deps): bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/SharpDicom1 reaction
- Mar 16, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/nccid1 reaction
- Mar 15, 2026chore(deps): bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/filestats1 reaction
- Mar 15, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/MCPsharp1 reaction
- Mar 15, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/SynthEHR1 reaction
- Mar 15, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/TypeGuesser1 reaction
- Mar 15, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/LineReader1 reaction
- Mar 15, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/DicomTypeTranslation1 reaction
- Mar 12, 2026Bump dotnet-sdk from 10.0.103 to 10.0.201 — jas88/dcm2csv1 reaction
- Mar 11, 2026chore(deps): bump dotnet-sdk from 10.0.103 to 10.0.200 — jas88/PACSify1 reaction
- Mar 7, 2025Use Microsoft Testing Platform — martincostello/alexa-london-travelsays it tested it1 reaction · 3 comments
- Feb 17, 2025Bump the mstest group across 1 directory with 2 updates — kusl/fluentassertions1 reaction · 9 comments
- May 25, 2026Bump MSTest.TestAdapter and MSTest.TestFramework — fluentassertions/fluentassertions0 reactions · 8 comments
- May 18, 2026Bump the mstest group with 2 updates — fluentassertions/fluentassertions0 reactions · 3 comments
- May 11, 2026Bump MSTest.TestAdapter and MSTest.TestFramework — fluentassertions/fluentassertions0 reactions · 4 comments
On Stack Overflow · 27
- Feb 24, 2019App.config not being loaded in .Net Core MSTests project35 votes
- Dec 21, 2022Visual Studio Test Explorer doesn't run tests after changes31 votes
- Dec 3, 2019DataTestMethod vs TestMethod19 votes
- May 5, 2020ConfigurationManager.AppSettings is always empty17 votes
- Dec 4, 2020The type or namespace name 'PrivateObject' could not be found6 votes
22 more on Stack Overflow
- Nov 2, 2018'TestContext' does not contain a definition for 'DataRow'5 votes
- Jun 15, 2019Get TestResult in MSTest TestCleanup in C#4 votes
- Nov 14, 2024Does a [TestMethod] have to be public? What consequences are there if it isn't?3 votes
- Aug 26, 2020'Inconclusive' tests are reported as 'not run' by Azure DevOps3 votes
- Sep 18, 2018Guidance for running tests using MSTest v1 in Visual Studio 2017 15.8.13 votes
- Nov 29, 2017Long running unit test marked as passed fails TFS build - Object 'xxx.rem' has been disconnected or does not exist at the server.**3 votes
- Jun 25, 2021use PrivateObject in .net 5 from nugget MSTest.TestFramework2 votes
- Jan 7, 2020MS Test V2 - ClassInitialize behavior when fail2 votes
- Oct 30, 2019Test Methods and Test Class in compiled DLL2 votes
- Jan 15, 2019Use configuration file to specify connection string to define a data source in unit test2 votes
- May 2, 2017Where is MSTest V22 votes
- Oct 15, 2022How to unit test private method in .Net Core application which does not support "PrivateObject"1 vote
- Oct 5, 2022Only first DataRow gets executed in MSTest1 vote
- Feb 4, 2021MS Tests DataTestMethod Timeout for a single test1 vote
- Feb 13, 2020PrivateObject class not found in .Net Core 2.01 vote
- Jan 4, 2020MsTest TestCleanup method not called when an unhandled exception is thrown1 vote
- May 3, 2019Where do I find TestContext.AddResultFile()?1 vote
- Apr 24, 2017Inherited test class from generic base is ignored in MSTest1 vote
- Aug 20, 2025Use MSTest.Sdk for TestHelper0 votes · 1 answer
- Mar 9, 2022Test results are published correctly but not reflected in pipeline Results - Azure devops (Yaml)0 votes
- Dec 22, 2021Testing with empty string using DataRow in MsTestV20 votes
- Feb 6, 2018Why does Visual Studio Always pull in the latest version Unit Test Framework within a solution when older versions are already included0 votes
In this repository62 other files it gives its agents.
- AGENTS.mdAGENTS.md
- eng/common/AGENTS.mdAGENTS.md
- .github/copilot-instructions.mdCopilot instructions
- assertion-qualitySkill
- binlog-failure-analysisSkill
- binlog-generationSkill
- build-parallelismSkill
- build-perf-baselineSkill
- build-perf-diagnosticsSkill
- check-bin-obj-clashSkill
- code-testing-agentSkill
- code-testing-extensionsSkill
- code-testingSkill
- copy-to-output-directorySkill
- coverage-analysisSkill
- crap-scoreSkill
- csharp-expertSkill
- csharp-refactoringSkill
- curation-exceptionsSkill
- detect-static-dependenciesSkill
- directory-build-organizationSkill
- eliminate-redundant-restoresSkill
- eval-performanceSkill
- extension-pointsSkill
- filter-syntaxSkill
- generate-testability-wrappersSkill
- grade-testsSkill
- including-generated-filesSkill
- incremental-buildSkill
- item-managementSkill
- migrate-static-to-wrapperSkill
- msbuild-antipatternsSkill
- msbuild-modernizationSkill
- msbuild-serverSkill
- msbuildSkill
- mtp-hot-reloadSkill
- platform-detectionSkill
- property-patternsSkill
- resolve-project-referencesSkill
- run-testsSkill
- scaffold-dotnet-test-projectSkill
- setup-local-sdkSkill
- target-authoringSkill
- testability-obstacleSkill
- test-analysis-extensionsSkill
- test-anti-patternsSkill
- test-gap-analysisSkill
- test-smell-detectionSkill
- test-taggingSkill
- test-value-evidenceSkill
- troubleshoot-binlog-mcpSkill
- writing-mstest-testsSkill
- agentic-workflowsSkill
- behavior-preserving-refactorSkill
- code-reviewSkill
- feature-deliverySkill
- post-release-activitiesSkill
- session-delivery-hygieneSkill
- testfx-acceptance-validationSkill
- testfx-localization-validationSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

