dotnet/run-tests
> Recommend or run the exact `dotnet test` command. ALWAYS use when the user asks to run, filter, or troubleshoot .NET tests or wants the precise command, flags, or argument order — the right syntax depends on the test platform (VSTest vs Microsoft.Testing.Platform) and SDK version and is specific class, category, or trait) via filters; a single framework in a multi-TFM project (`--framework`); TRX reports; crash or hang dumps; whether MTP args need the `--` separator (SDK 8/9) or pass directly (SDK 10+); diagnosing why `dotnet test` fails or uses wrong argument syntax. Detects the platform (VSTest vs MTP) and framework (MSTest/xUnit/NUnit/TUnit), then picks the matching command and filter flag (--filter, --filter-class, --filter-trait, --filter-query, code-testing-agent), iterating on failing tests without rebuilding (use mtp-hot-reload), CI/CD config, or debugging test logic.
npx skills add https://github.com/dotnet/skills --skill run-tests
Detect the test platform and framework, run tests, and apply filters using dotnet test.
writing-mstest-tests for MSTest, or general coding assistance for other frameworks)migrate-vstest-to-mtp)mtp-hot-reload)| Input | Required | Description |
|-------|----------|-------------|
| Project or solution path | No | Path to the test project (.csproj) or solution (.sln, .slnf, .slnx). Defaults to current directory. |
| Filter expression | No | Filter expression to select specific tests |
| Target framework | No | Target framework moniker to run against (e.g., net8.0) |
These are the most common agent mistakes. Internalize before proceeding:
| Rule | Why |
|------|-----|
| Do NOT use --logger trx for MTP projects | MTP uses --report-trx (requires the TrxReport extension package) |
| Do NOT use --report-trx for VSTest projects | VSTest uses --logger trx |
| Do NOT use -- --arg on .NET SDK 10+ | SDK 10+ passes MTP args directly: dotnet test --project . --report-trx |
| Do NOT omit -- on .NET SDK 8/9 with MTP | SDK 8/9 requires the separator: dotnet test -- --report-trx |
| Do NOT use --filter "ClassName=..." with xUnit v3 on MTP | xUnit v3 on MTP uses --filter-class, --filter-method, --filter-trait |
| Do NOT use bare positional path on SDK 10+ | Use --project <path> or --solution <path> instead |
| Do NOT use --blame for MTP projects | MTP uses --blame-crash and --blame-hang-timeout separately (each requires its extension package) |
| Do NOT use --collect "Code Coverage" for MTP | MTP uses --coverage (requires the CodeCoverage extension package) |
| Platform | SDK | Command pattern |
|----------|-----|----------------|
| VSTest | Any | dotnet test [<path>] [--filter <expr>] [--logger trx] |
| MTP | 8 or 9 | dotnet test [<path>] -- <MTP_ARGS> |
| MTP | 10+ | dotnet test --project <path> <MTP_ARGS> |
Detection files to always check (in order): global.json -> .csproj -> Directory.Build.props -> Directory.Packages.props
If the prompt names a subset of tests (e.g., "integration tests", "smoke tests", a specific class, a specific TFM), plan to apply the matching filter / --framework in Step 3 — do not run the whole suite.
dotnet --version in the project directory to determine the SDK version. This accounts for global.json SDK pinning.global.json — on .NET SDK 10+, "test": { "runner": "Microsoft.Testing.Platform" } is the authoritative MTP signal. If present, the project uses MTP and SDK 10+ syntax (no -- separator)..csproj, Directory.Build.props, and Directory.Packages.props for framework packages and MTP properties. Always check all three files — MTP properties are frequently set in Directory.Build.props rather than individual .csproj files.platform-detection skill.What to look for in each file:
| File | Look for | Indicates |
|------|----------|-----------|
| global.json | "test": { "runner": "Microsoft.Testing.Platform" } | MTP on SDK 10+ |
| global.json | "sdk": { "version": "..." } | SDK version (determines -- separator behavior) |
| .csproj | <TestingPlatformDotnetTestSupport>true | MTP on SDK 8/9 |
| .csproj | MSTest, xunit.v3, NUnit, TUnit packages | Framework identity |
| .csproj | Microsoft.NET.Test.Sdk + test adapter | VSTest (unless overridden by MTP signals above) |
| .csproj | <TargetFrameworks> (plural) | Multi-TFM — may need --framework |
| Directory.Build.props | <TestingPlatformDotnetTestSupport>true | MTP on SDK 8/9 (often set here, not in .csproj) |
| Directory.Packages.props | Centrally managed test package versions | Framework identity for CPM repos |
Quick detection summary:
| Signal | Means |
|--------|-------|
| global.json has "test": { "runner": "Microsoft.Testing.Platform" } | MTP on SDK 10+ — pass args directly, no -- |
| <TestingPlatformDotnetTestSupport>true in csproj or Directory.Build.props | MTP on SDK 8/9 — pass args after -- |
| Neither signal present | VSTest |
dotnet test [<PROJECT> | <SOLUTION> | <DIRECTORY> | <DLL> | <EXE>]
Common flags:
| Flag | Description |
|------|-------------|
| --framework <TFM> | Target a specific framework in multi-TFM projects (e.g., net8.0) |
| --no-build | Skip build, use previously built output |
| --filter <EXPRESSION> | Run selected tests (see Step 3) |
| --logger trx | Generate TRX results file |
| --collect "Code Coverage" | Collect code coverage using Microsoft Code Coverage (built-in, always available) |
| --blame | Enable blame mode to detect tests that crash the host |
| --blame-crash | Collect a crash dump when the test host crashes |
| --blame-hang-timeout <duration> | Abort test if it hangs longer than duration (e.g., 5min) |
| -v <level> | Verbosity: quiet, minimal, normal, detailed, diagnostic |
With <TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>, dotnet test bridges to MTP but uses VSTest-style argument parsing. MTP-specific arguments must be passed after --:
dotnet test [<PROJECT> | <SOLUTION> | <DIRECTORY> | <DLL> | <EXE>] -- <MTP_ARGUMENTS>
With the global.json runner set to Microsoft.Testing.Platform, dotnet test natively understands MTP arguments without --:
dotnet test
[--project <PROJECT_OR_DIRECTORY>]
[--solution <SOLUTION_OR_DIRECTORY>]
[--test-modules <EXPRESSION>]
[<MTP_ARGUMENTS>]
Examples:
# Run all tests in a project
dotnet test --project path/to/MyTests.csproj
# Run all tests in a directory containing a project
dotnet test --project path/to/
# Run all tests in a solution (sln, slnf, slnx)
dotnet test --solution path/to/MySolution.sln
dotnet test --solution path/to/MySolution.slnf
dotnet test --solution path/to/MySolution.slnx
# Run all tests in a directory containing a solution
dotnet test --solution path/to/
# Run with MTP flags
dotnet test --project path/to/MyTests.csproj --report-trx --blame-hang-timeout 5min
> Note: The .NET 10+ dotnet test syntax does not accept a bare positional argument like the VSTest syntax. Use --project, --solution, or --test-modules to specify the target.
These flags apply to MTP on both SDK versions. On SDK 8/9, pass after --; on SDK 10+, pass directly.
> Important: dotnet test/MSBuild flags such as --framework, --no-build, --configuration, and --verbosity are consumed by dotnet test itself (they drive restore/build/host selection) and always go BEFORE --, regardless of platform or SDK. Only MTP test-platform arguments go after -- on SDK 8/9. For example: dotnet test --framework net9.0 -- --report-trx (built-in flag before --, MTP extension flag after).
Built-in flags (always available):
| Flag | Description |
|------|-------------|
| --results-directory <DIR> | Directory for test result output |
| --diagnostic | Enable diagnostic logging for the test platform |
| --diagnostic-output-directory <DIR> | Directory for diagnostic log output |
Extension-dependent flags (require the corresponding extension package to be registered):
| Flag | Requires | Description |
|------|----------|-------------|
| --filter <EXPRESSION> | Framework-specific (not all frameworks support this) | Run selected tests (see Step 3) |
| --report-trx | Microsoft.Testing.Extensions.TrxReport | Generate TRX results file |
| --report-trx-filename <FILE> | Microsoft.Testing.Extensions.TrxReport | Set TRX output filename |
| --blame-hang-timeout <duration> | Microsoft.Testing.Extensions.HangDump | Abort test if it hangs longer than duration (e.g., 5min) |
| --blame-crash | Microsoft.Testing.Extensions.CrashDump | Collect a crash dump when the test host crashes |
| --coverage | Microsoft.Testing.Extensions.CodeCoverage | Collect code coverage using Microsoft Code Coverage |
> Some frameworks (e.g., MSTest) bundle common extensions by default. Others may require explicit package references. If a flag is not recognized, check that the corresponding extension package is referenced in the project.
MTP test projects are standalone executables. Beyond dotnet test, they can be run directly:
# Build and run
dotnet run --project <PROJECT_PATH>
# Run a previously built DLL
dotnet exec <PATH_TO_DLL>
# Run the executable directly (Windows)
<PATH_TO_EXE>
These alternative invocations accept MTP command line arguments directly (no -- separator needed).
See the filter-syntax skill for the complete filter syntax for each platform and framework combination. Key points:
dotnet test --filter <EXPRESSION> with =, !=, ~, !~ operators--filter syntax as VSTest; pass after -- on SDK 8/9, directly on SDK 10+--filter-class, --filter-method, --filter-trait (not VSTest expression syntax). For a single combined expression (e.g., a class-name pattern AND a trait), use --filter-query with the xUnit v3 query filter language: path segments /<assembly>/<namespace>/<class>/<method> with * wildcards and a [Trait=Value] qualifier — for example dotnet test -- --filter-query "/*/*/*IntegrationTests*/*[Category=Smoke]". See the filter-syntax skill for the full query language.--treenode-filter with path-based syntaxWhen the prompt names a subset of tests by category (e.g., "integration tests", "unit tests", "smoke tests", "fast tests"), do not run all tests — translate the user's vocabulary into the platform-appropriate filter:
| Framework | Attribute | Filter property |
|-----------|-----------|-----------------|
| MSTest | [TestCategory("Integration")] | TestCategory |
| NUnit | [Category("Integration")] | TestCategory (mapped) |
| xUnit v2 | [Trait("Category", "Integration")] | Category |
| xUnit v3 | [Trait("Category", "Integration")] | Category (use --filter-trait) |
| TUnit | [Category("Integration")] | Category |
| Platform | SDK | Command |
|----------|-----|---------|
| VSTest (MSTest) | any | dotnet test --filter "TestCategory=Integration" |
| MTP (MSTest) | 8 or 9 | dotnet test -- --filter "TestCategory=Integration" |
| MTP (MSTest) | 10+ | dotnet test --filter "TestCategory=Integration" |
| MTP (xUnit v3) | 8 or 9 | dotnet test -- --filter-trait "Category=Integration" |
| MTP (xUnit v3) | 10+ | dotnet test --filter-trait "Category=Integration" |
| MTP (TUnit) | 8 or 9 | dotnet test -- --treenode-filter "/*/*/*/*[Category=Integration]" |
--filter "FullyQualifiedName~Integration").dotnet test invocation was used for the detected platform and SDK version| Pitfall | Solution |
|---------|----------|
| Missing Microsoft.NET.Test.Sdk in a VSTest project | Tests won't be discovered. Add <PackageReference Include="Microsoft.NET.Test.Sdk" /> |
| Using VSTest --filter syntax with xUnit v3 on MTP | xUnit v3 on MTP uses --filter-class, --filter-method, etc. -- not the VSTest expression syntax |
| Passing MTP args without -- on .NET SDK 8/9 | Before .NET 10, MTP args must go after --: dotnet test -- --report-trx |
| Using -- --arg separator on .NET SDK 10+ | SDK 10+ passes MTP args directly — do NOT use -- separator |
| Using --logger trx for MTP or --report-trx for VSTest | Each platform has its own TRX flag — check the Critical Rules table |
| Only checking .csproj for MTP signals | Always check Directory.Build.props and Directory.Packages.props too — MTP properties are frequently set there |
| Using bare positional path argument on SDK 10+ | SDK 10+ requires named flags: --project <path> or --solution <path> |
Common error messages and how to resolve them:
| Error | Cause | Fix |
|-------|-------|-----|
| No test is available or No test matches the given testcase filter | Wrong filter syntax for the platform/framework, or tests not discovered | Verify filter syntax matches the platform (see filter-syntax skill). For discovery issues, check that the test SDK and adapter packages are installed |
| The --report-trx option is unrecognized | MTP extension package not referenced, or using MTP flag on a VSTest project | Add <PackageReference Include="Microsoft.Testing.Extensions.TrxReport" /> for MTP, or use --logger trx for VSTest |
| The --blame-hang-timeout option is unrecognized | Missing HangDump extension on MTP | Add <PackageReference Include="Microsoft.Testing.Extensions.HangDump" /> |
| error NETSDK1045: The current .NET SDK does not support targeting .NET X.0 | SDK version in global.json doesn't match the project's target framework | Update global.json SDK version or install the required SDK |
| The test runner process exited with non-zero exit code | MTP test host crashed or test failure | Run with --blame-crash (MTP) or --blame (VSTest) to collect a crash dump for diagnosis |
| No test source files were found / No test project found | dotnet test can't find a test project in the given path | Specify the path explicitly: dotnet test <path/to/project.csproj> (VSTest) or dotnet test --project <path> (SDK 10+) |
| Tests discovered but 0 executed | Filter expression matches no tests | Double-check filter property names and values. Common typo: TestCategory (MSTest) vs Category (NUnit) vs trait syntax (xUnit) |
| Using -- for MTP args on .NET SDK 10+ | On .NET 10+, MTP args are passed directly: dotnet test --project . --blame-hang-timeout 5min — do NOT use -- --blame-hang-timeout |
| Multi-TFM project runs tests for all frameworks | Use --framework <TFM> to target a specific framework |
| global.json runner setting ignored | Requires .NET 10+ SDK. On older SDKs, use <TestingPlatformDotnetTestSupport> MSBuild property instead |
| TUnit --treenode-filter not recognized | TUnit is MTP-only. On .NET SDK 10+ use dotnet test; on older SDKs use dotnet run since VSTest-mode dotnet test does not support TUnit |
Take dotnet/run-tests from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.