run-dotnet-tests
Run .NET Tests
Purpose
Provide standardized instructions for running .NET 10 tests correctly. Ensures agents use the scripts/test-with-timeout.sh wrapper instead of direct dotnet test calls, which fail due to .NET 10's dual test runner architecture.
When to Use This Skill
- Before committing code changes that affect C# code
- When verifying bug fixes or new features
- When running targeted tests during development
- When the full test suite must pass before marking work complete
Hard Rules
Must
- ALWAYS use
scripts/test-with-timeout.shwrapper - never calldotnet testdirectly - Run tests from the repository root (the wrapper handles directory changes automatically)
- Use
--solution src/tfplan2md.slnxfor full test suite runs - Use
--projectwith relative paths fromsrc/directory (e.g.,--project tests/Oocx.TfPlan2Md.TUnit/) - Wait for test completion and check exit code (0 = pass, non-zero = fail)
- For snapshot test changes, use the
update-test-snapshotsskill instead of manual edits
Must Not
- NEVER run
dotnet testdirectly from command line - it will fail withMSB1001: Unknown switcherrors from repo root - Never manually edit snapshot files in
src/tests/Oocx.TfPlan2Md.TUnit/TestData/Snapshots/- use theupdate-test-snapshotsskill - Never ignore test failures or skip tests to make CI pass
- Never modify test expectations to match broken output - fix the code, not the tests
.NET 10 Dual Test Runner Issue
.NET 10 introduced two distinct test runners with incompatible CLI flags:
| Working Directory | Runner Mode | --solution |
--project |
--treenode-filter |
Result |
|---|---|---|---|---|---|
Repo root (/) |
VSTest | ❌ | ❌ | ❌ | MSBuild error MSB1001: Unknown switch |
src/ (where global.json lives) |
Microsoft.Testing.Platform | ✅ | ✅ | ✅ | Works correctly |
The scripts/test-with-timeout.sh wrapper automatically:
- Changes to the
src/directory (whereglobal.jsonwith"runner": "Microsoft.Testing.Platform"exists) - Normalizes any
src/-prefixed paths in arguments - Enforces a timeout to prevent hung test runs (default 120 seconds)
Why direct dotnet test fails: Running from repo root activates VSTest mode (no global.json), which doesn't support --solution, --project, or --treenode-filter flags. The wrapper ensures tests run from src/ directory to activate Microsoft.Testing.Platform mode.
Common Test Commands
Run Full Test Suite
scripts/test-with-timeout.sh -- dotnet test --solution src/tfplan2md.slnx
Run Tests with Build
scripts/test-with-timeout.sh -- dotnet test --solution src/tfplan2md.slnx --configuration Release --verbosity normal
Run Tests Without Build (faster when already built)
scripts/test-with-timeout.sh -- dotnet test --solution src/tfplan2md.slnx --no-build
Override Timeout (for slow tests)
scripts/test-with-timeout.sh --timeout-seconds 300 -- dotnet test --solution src/tfplan2md.slnx
Run Specific Project Tests
scripts/test-with-timeout.sh -- dotnet test --project tests/Oocx.TfPlan2Md.TUnit/
TUnit Test Filtering
Important: This project uses TUnit (not xUnit). TUnit uses --treenode-filter instead of --filter. All TUnit-specific flags must come after -- in the wrapper command.
Filter by Class Name (hierarchical pattern)
scripts/test-with-timeout.sh -- dotnet test --project tests/Oocx.TfPlan2Md.TUnit/ --treenode-filter /*/*/MarkdownRendererTests/*
Filter by Test Method Name
scripts/test-with-timeout.sh -- dotnet test --project tests/Oocx.TfPlan2Md.TUnit/ --treenode-filter /*/*/*/Render_ValidPlan_ContainsSummarySection
Filter Pattern Explanation
TUnit uses hierarchical path patterns:
/*/*/*/TestMethodName- Match specific test method/*/*/ClassName/*- Match all tests in a class/*/Namespace.ClassName/*- Match by namespace and class
Workflow Integration
When to Run Tests
-
During Development (after each meaningful change):
# Run targeted tests for the area you modified scripts/test-with-timeout.sh -- dotnet test --project tests/Oocx.TfPlan2Md.TUnit/ --treenode-filter /*/*/YourTestClass/* -
Before Committing (C# code changes only):
# Run full test suite to ensure no regressions scripts/test-with-timeout.sh -- dotnet test --solution src/tfplan2md.slnx --no-build -
Skip Tests When (documentation/agent instructions only):
- Changes are limited to
.github/agents/,.github/skills/,.github/copilot-instructions.md, ordocs/ - No C# code was modified
- The test suite doesn't validate these file types
- Changes are limited to
Handling Test Failures
- Read the failure output - TUnit provides detailed error messages
- Identify the failing test - Look for the test method name and class
- Run the specific failing test - Use
--treenode-filterto isolate it - Debug and fix - Fix the code (never modify test expectations unless the test itself is wrong)
- Re-run tests - Verify the fix works
- Run full suite - Ensure no regressions before committing
Snapshot Test Changes
If tests fail because snapshot files need updating (intentional output changes):
- Use the
update-test-snapshotsskill - Never manually edit snapshot files - Verify the new output is correct - Review the diff carefully
- Include
SNAPSHOT_UPDATE_OKin commit message - Document the intentional change - Re-run tests - Confirm all tests pass with new snapshots
Exit Codes
| Exit Code | Meaning | Action |
|---|---|---|
| 0 | All tests passed | Proceed with commit |
| 1-123 | Test failures | Fix failing tests before committing |
| 124 | Timeout | Increase timeout with --timeout-seconds or investigate hung tests |
| 125 | Wrapper error | Check command syntax |
Golden Example
Complete workflow for implementing a feature:
# 1. Build the project
dotnet build
# 2. Run targeted tests during development
scripts/test-with-timeout.sh -- dotnet test --project tests/Oocx.TfPlan2Md.TUnit/ --treenode-filter /*/*/MyFeatureTests/*
# 3. Make changes, run tests again
scripts/test-with-timeout.sh -- dotnet test --project tests/Oocx.TfPlan2Md.TUnit/ --treenode-filter /*/*/MyFeatureTests/*
# 4. Before committing, run full suite
scripts/test-with-timeout.sh -- dotnet test --solution src/tfplan2md.slnx --no-build
# 5. If all pass, commit changes
git add .
git commit -m "feat: implement my feature"
Troubleshooting
Error: MSBuild error MSB1001: Unknown switch
Cause: Running dotnet test directly from repo root (VSTest mode doesn't support --solution/--project flags)
Solution: Use scripts/test-with-timeout.sh wrapper
Error: Timeout after 120 seconds
Cause: Tests taking longer than default timeout
Solution: Increase timeout with --timeout-seconds 300 (or higher)
Error: Test not found with --treenode-filter
Cause: Incorrect filter pattern or test doesn't exist Solution:
- List all tests first:
scripts/test-with-timeout.sh -- dotnet test --list-tests - Verify filter pattern matches TUnit hierarchical structure
Snapshot test failures after output changes
Cause: Intentional output format changes
Solution: Use update-test-snapshots skill to regenerate snapshots correctly