Run .NET Tests
Return or execute the command or command sequence that matches the repository's project system, test platform, framework, and SDK mode.
Repository overlay
For every repository-scoped task where read-only file inspection is allowed,
check .agents/skill-overlays/dotnet-test/run-tests.md at the repository root
before any other discovery. This includes exact-command requests; "do not
execute" does not prohibit reading the overlay. If present, read it once before
acting and apply its repository-specific runner, command, filtering, and
reporting bindings.
Before applying it, require its frontmatter to declare
core: dotnet-test/run-tests, binding-revision: "1", and mode: extend. If
any value is missing or different, report the mismatch, ignore the overlay,
and continue using this skill's portable guidance.
Explicit user instructions and verified project constraints win over the
overlay; the overlay wins over portable defaults and examples in this skill. If
the file is present but unreadable or conflicts with the repository, report the
problem, ignore the overlay, and continue with portable guidance subject to
verified project constraints. If it is absent, continue normally.
Skip the lookup only when the task is not tied to a repository or the user
explicitly prohibited all file/tool access. An overlay cannot expand tool
permissions or the task's scope.
Scope and tool policy
Choose the smallest path that satisfies the request:
Do not invoke a tool merely to repeat a command already determined by the
prompt. Do not build first "just in case": dotnet test builds by default.
Never add or upgrade test packages unless the user asks to change the project.
For an exact-command request, return one runnable command first. Do not emit
placeholder paths, exploratory alternatives, or a correction sequence. Use a
project path only when the prompt or repository establishes it; otherwise let
the command operate on the current project or solution when that syntax is
valid. Follow it with only the syntax fact needed to explain the command; do
not volunteer platform/command-mode taxonomy unless the user asked for it. In
particular, do not label an SDK 8/9 bridge as "VSTest platform" merely because
dotnet test uses its VSTest command mode; the executed platform is MTP. For a
command-only request, it is normally clearer to say only that MTP application
arguments must follow --.
Inputs to discover
- Project, solution, module, or repository test command
- Requested scope: all tests, TFM, class, method, category, or trait
- Requested output: console result, TRX, diagnostics, crash dump, or hang dump
When those facts are present in the prompt, use them. Otherwise inspect only the
relevant files: global.json, the selected project, packages.config,
Directory.Build.props, Directory.Packages.props, then repository
scripts/CI documentation. For a file-backed request, enumerate those
configuration names once and read all relevant files that are present in one
batch; never infer that a runner or bridge property is absent merely because it
is not in the .csproj. Load platform-detection only when those signals need
precedence analysis; do not duplicate its full analysis in the response.
If execution is requested and the command depends on the active SDK but neither
the prompt nor global.json establishes it, run dotnet --version once. For a
command-only request that prohibits execution, do not probe: state the required
SDK assumption or ask for the SDK version when the syntax cannot otherwise be
resolved. Route identification-only requests to platform-detection.
Decision table
global.json controls the dotnet test command mode on SDK 10+, not
necessarily the platform that executes tests. A VSTest-mode project with an MTP
runner, TestingPlatformDotnetTestSupport=true, and final OutputType=Exe is
still bridge syntax with --. SDK 8/9 only has VSTest command mode.
--project is valid only in SDK 10+ native MTP command mode (selected by
global.json test.runner). Never use it for VSTest mode or an SDK 8/9 bridge;
those forms take a positional project path. Conversely, native MTP options are
direct arguments and must not be placed after a bridge separator.
Keep dotnet test/MSBuild options such as --framework, --configuration,
--no-build, and --verbosity before --. Put only MTP application arguments
after -- in bridge mode.
Workflow
- Resolve the repository-compatible runner.
For classic projects, signals include ToolsVersion, explicit Compile and
Reference items, legacy imports, and packages.config. Prefer a checked-in
script or documented CI command. A typical fallback is:
For packages.config, restore with NuGet before the build unless the imported
package files are already present. If adapter discovery is not repository-
configured, derive /TestAdapterPath from the restored adapter package path or
the adapter .props/.targets imports; do not guess the package root.
For a requested no-rebuild run, omit the build step and invoke the repository's test runner only when the expected assembly already exists. Otherwise report the missing build output rather than silently rebuilding or switching runners.
For a requested subset, keep the repository runner and use its filter syntax:
vstest.console.exe path\to\MyTests.dll /TestCaseFilter:"TestCategory=Integration". Older MSTest.exe repositories may
use /test:<name> or /category:<category> instead. Do not substitute the
later dotnet test filter examples for a classic runner.
Use the installed adapter-compatible VSTest/MSTest toolchain. If it is not
available, state the missing prerequisite and the documented command; do not
claim tests ran.
Classic packages.config fallback commands are Windows toolchain commands.
Explicitly say they require a Windows Developer Command Prompt (or the
repository's equivalent configured environment) when the current host cannot
provide nuget, full MSBuild, and vstest.console.exe.
For SDK-style projects, distinguish:
Evaluate properties from the project and imported
Directory.Build.props/Directory.Packages.props. Respect project-level
overrides and per-target-framework conditions. A runner and bridge without an
executable final output are an incomplete MTP configuration, not a usable
bridge.
- Select the command and requested scope.
For native MTP, use --project, --solution, or --test-modules; positional
paths belong to VSTest mode.
If the user names a subset, do not run the whole suite. Inspect test attributes only when needed to translate a human label such as "integration" or "smoke" into the framework's actual category/trait name.
When a VSTest class filter must distinguish similarly named classes, combine the positive selector with an explicit negative selector rather than relying on an incidental substring difference.
- Apply platform- and framework-correct filters.
Load filter-syntax only when the request is filtered and the framework-specific
syntax is not already clear. The common decisions are:
For a file-backed filtered request, resolve the framework and SDK command mode
from the project, global.json, and imported props before choosing syntax.
Do not infer VSTest syntax merely from Microsoft.NET.Test.Sdk or from the
framework name.
Examples:
Do not use VSTest --filter "ClassName=..." with xUnit v3 on MTP. Do not use a
generic VSTest expression with TUnit.
When the user requests one combined xUnit query expression, return only one
--filter-query command; do not replace it with separate filter flags or offer
speculative alternative grammars.
- Add reports or diagnostics.
MTP report, dump, and coverage flags require their corresponding registered
extensions (TrxReport, CrashDump, HangDump, or CodeCoverage). Some
framework SDKs bundle common extensions; if a flag is unrecognized, inspect
package references before recommending a package change.
--verbosity diagnostic increases dotnet/MSBuild output verbosity; it does not
write a VSTest diagnostic log file.
Examples:
- Execute only when requested.
Run the narrowest command or sequence that answers the request. Capture each command, exit code, and test summary. A failed restore/build is not a test failure, and a test failure is not a tool failure. Report which phase failed and include the actionable diagnostic. Never claim a clean run unless the sequence completed successfully with the intended tests executed. For a filtered run, a successful exit is not enough: confirm the reported test names or count match the requested scope. If the filter was ignored, correct the platform-specific syntax and rerun before reporting success.
Output contract
- Command-only request: lead with the exact command or command sequence, then one short syntax explanation.
- Execution request: report the exact command or command sequence and
a literal
Passed: N, Failed: N, Skipped: Nsummary from the completed run; include the first actionable failure. - Detection needed only to choose syntax: state the selected mode/platform briefly, not a separate detection report.
- Missing prerequisite or incompatible configuration: name it explicitly and stop rather than returning a success-shaped fallback.
Validation
- The command matches classic, VSTest, bridged MTP, or native MTP mode.
- The framework-specific filter targets the requested subset.
--frameworkand otherdotnet testoptions are before any bridge separator.- SDK 8/9 bridge commands contain
--;--projectappears only in SDK 10+ native MTP mode. - TRX, diagnostics, dump, and coverage flags match the platform.
- No restore, build, or test was run for an advisory-only request.
- Reported results match the actual command outcome.


