Use the open-source free `coverlet` toolchain for .NET code coverage.
Run .NET Tests
Run .NET tests or give the exact repository-compatible command. Use for "run the tests", one test/class/category/trait, one target framework, "what dotnet test command?", `--no-build`, `--diag`, diagnostic logs, classic packages.config or MSTest.exe, TRX or coverage collection, crash/hang dumps, filter mismatch, `--filter-query`, a single combined filter expression, or unrecognized options. Handles VSTest and bridged/native Microsoft.Testing.Platform across MSTest/xUnit/NUnit/TUnit, including NUnit bridge filters, xUnit v3 class/trait/query filters, multi-TFM, and argument order. For identification-only requests, use platform-detection. DO NOT USE for writing tests, hot-reload/no-rebuild loops, migration, CI, coverage analysis, or debugging test logic.
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:
nuget restore MySolution.sln
MSBuild.exe MySolution.sln /t:Build /p:Configuration=Debug
vstest.console.exe path\to\MyTests.dll /TestAdapterPath:path\to\adapter\build\<tfm>
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.
For SDK-style projects, distinguish:
| Signal | Meaning | |---|---| | SDK 10+ global.json selects Microsoft.Testing.Platform | Native MTP command mode | | VSTest mode + enabled MTP runner + final TestingPlatformDotnetTestSupport=true + final OutputType=Exe | Executable VSTest-to-MTP bridge | | VSTest mode without a complete runner-and-bridge combination | VSTest | | Microsoft.NET.Test.Sdk plus adapter, without stronger MTP signals | VSTest | | TUnit | MTP-only; use a configured bridge/native mode or the test executable |
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.
# VSTest mode
dotnet test path/to/Tests.csproj
# VSTest mode that bridges to MTP
dotnet test path/to/Tests.csproj -- <MTP_OPTIONS>
# Native MTP mode on SDK 10+
dotnet test --project path/to/Tests.csproj <MTP_OPTIONS>
# One target framework; this stays before the bridge separator
dotnet test path/to/Tests.csproj --framework net9.0 -- <MTP_OPTIONS>
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.
| Platform / framework | Filter | |---|---| | VSTest with MSTest, xUnit v2, or NUnit | --filter "<property expression>" | | MTP with MSTest or NUnit | Same expression; after -- in bridge mode, direct in native mode | | MTP with xUnit v3 | --filter-class, --filter-method, --filter-trait, or one --filter-query for a combined expression | | MTP with TUnit | --treenode-filter path expression |
Examples:
# VSTest MSTest/NUnit
dotnet test --filter "FullyQualifiedName~OrderServiceTests&TestCategory=Unit"
# SDK 8/9 or SDK 10 VSTest-mode MTP bridge, xUnit v3
dotnet test -- --filter-trait "Category=Integration"
# Native MTP, xUnit v3
dotnet test --project Tests.csproj --filter-class "*ShoppingCartTests*"
# One xUnit v3 combined expression
dotnet test -- --filter-query "/*/*/*Integration*/*[Category=Smoke]"
# TUnit on SDK 8/9 with a configured VSTest-to-MTP bridge
dotnet test -- --treenode-filter "/*/*/SmsNotificationTests/*"
# TUnit executable fallback when no bridge is configured
dotnet run --project Tests.csproj -- --treenode-filter "/*/*/SmsNotificationTests/*"
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.
| Outcome | VSTest | MTP | |---|---|---| | TRX | SDK-style: --logger "trx;LogFileName=<name>.trx" when an exact output file is requested, otherwise --logger trx; standalone vstest.console.exe: /Logger:trx; MSTest.exe: repository-documented results option | --report-trx | | Results directory | --results-directory <dir> | --results-directory <dir> | | Diagnostic log | --diag <file> | --diagnostic --diagnostic-output-directory <dir> | | Crash dump | --blame-crash | --crashdump | | Hang timeout | --blame-hang --blame-hang-timeout 5min | --hangdump --hangdump-timeout 5min | | Code coverage | --collect "Code Coverage" | --coverage |
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:
# VSTest TRX
dotnet test Tests.csproj --logger "trx;LogFileName=TestResults.trx"
# MTP bridge TRX
dotnet test Tests.csproj -- --report-trx
# Native MTP TRX and hang detection
dotnet test --project Tests.csproj --report-trx --hangdump --hangdump-timeout 5min
- 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.
Related skills
Write, run, or repair .NET tests that use MSTest.
Write, run, or repair .NET tests that use NUnit.