A .NET global tool that wraps dotnet test, captures TRX results, tracks test history, and displays regression charts.
- Wraps
dotnet testand streams output in real-time - Automatically captures TRX test results
- Tracks test history per project and command signature
- Shows pass/fail bar charts across runs
- Detects regressions (tests that passed before but now fail)
- Detects fixes (tests that failed before but now pass)
- Supports color and non-color (CI/piped) output modes
- Default 20s per-test timeout (detects and reports hung tests)
- Automatic hang isolation - when a hang is detected, automatically drills down to find the culprit
- Displays test hierarchy tree using Spectre.Console for better visualization
- Easy filter syntax for running specific tests
dotnet tool install -g Asynkron.TestRunnertestrunner # Runs: dotnet testtestrunner "MyClass" # Runs: dotnet test --filter "FullyQualifiedName~MyClass"
testrunner "Namespace.Test" # Matches any test containing that patterntestrunner -- dotnet test ./tests/MyTests
testrunner -- dotnet test --filter "Category=Unit"testrunner list # List all tests
testrunner list "Storage" # List tests matching 'Storage'testrunner --timeout 60 "SlowTests" # 60s hang timeout (default: 20s)
testrunner --timeout 0 # Disable hang detection
testrunner --help # Show helptestrunner stats # Default command history
testrunner stats -- dotnet test ./tests/MyTests # Specific command history
testrunner stats --history 5 # Last 5 runsWhen a test hang is detected during a normal run, testrunner automatically isolates and finds the hanging test:
testrunner "MyTests" # If hang detected, auto-isolatesThe isolation process:
- Displays a tree of all matching tests
- Breaks the tree into branches with fewer than 100 leaf tests (or the full tree if smaller)
- Runs each branch separately with the hang timeout and reports failing or hanging branches
You can also manually trigger isolation:
testrunner isolate # Find hanging test in all tests
testrunner isolate "language" # Find within tests matching 'language'
testrunner isolate --timeout 60 # Use 60s timeout (default: 30s)
testrunner isolate --parallel 4 # Run up to 4 batches concurrently
testrunner isolate -p # Use all CPU cores for parallel batchesFor large test suites, parallel isolation can significantly speed up the process:
testrunner isolate -p 4 "Tests" # Run 4 test batches concurrently
testrunner isolate --parallel # Auto-detect parallelism (CPU count)Parallel mode:
- Runs independent test batches concurrently
- Uses a semaphore to limit concurrent processes
- Thread-safe console output with progress indicators
- Falls back to sequential drilling when hanging tests are found
testrunner regressions # Compare last 2 runstestrunner clearFAILED
──────────────────────────────────────────────────
Passed: 132
Failed: 4
Skipped: 0
Total: 136
Duration: 2.3s
Pass Rate: 97.1%
Regressions (3):
✗ MyTests.SomeTest.ThatUsedToPass
✗ MyTests.AnotherTest.NowFailing
✗ MyTests.ThirdTest.Broken
Test History (4 runs)
──────────────────────────────────────────────────────────────────────
2025-12-30 10:04 █████████████████████████████X 135/136 (99.3%) ✗1
2025-12-30 10:03 █████████████████████████████X 132/136 (97.1%) ✗4
2025-12-30 10:00 █████████████████████████████X 135/136 (99.3%) ✗1
2025-12-30 10:00 █████████████████████████████X 135/136 (99.3%) ✗1
| Option | Description |
|---|---|
-t, --timeout <seconds> |
Per-test hang timeout (default: 20s for run, 30s for isolate) |
--timeout 0 |
Disable hang detection entirely |
| Option | Description |
|---|---|
-t, --timeout <seconds> |
Per-test timeout during isolation (default: 30s) |
-p, --parallel [N] |
Run N batches in parallel (default: 1, or CPU count if no N) |
Filters use FullyQualifiedName~<pattern> matching:
testrunner "MyClass" # Matches: MyNamespace.MyClass.AnyTest
testrunner "Integration" # Matches all tests containing "Integration"
testrunner "Tests.Unit" # Matches: MyApp.Tests.Unit.*History is stored in .testrunner/ and is tracked separately by:
- Project - hash of git repo root (or current directory)
- Command signature - hash of the test command (path + filters)
This means:
- Different repos have separate histories
--filter Aand--filter Bhave separate histories- Running the same command compares correctly
MIT
