Use the open-source free `coverlet` toolchain for .NET code coverage.
TUnit
Write, run, or repair .NET tests that use TUnit. Use when a repo uses `TUnit`, `TUnit.Playwright`, `[Test]`, `[Arguments]`, `ClassDataSource`, `SharedType.PerTestSession`, or Microsoft.Testing.Platform-based execution. Preserve TUnit's parallel-by-default model and constrain only destructive shared-state collisions. DO NOT USE FOR: xUnit projects; MSTest projects.
Trigger On
- the repo uses TUnit
- you need to add, run, debug, or repair TUnit tests
- the repo uses Microsoft.Testing.Platform-based test execution
- the repo uses
ClassDataSource<...>(Shared = SharedType.PerTestSession),TUnit.Playwright, or--treenode-filter
Workflow
- Confirm the project really uses TUnit and not a different MTP-based framework.
- Read the repo's real
testcommand fromAGENTS.md. If the repo has no explicit command yet, start withdotnet test PROJECT_OR_SOLUTION. - Keep the TUnit execution model intact:
- tests are source-generated at build time - tests run in parallel by default - on .NET 10, test modules also run in parallel by default up to Environment.ProcessorCount - built-in analyzers should remain enabled
- Choose the fixture level deliberately:
- plain TUnit tests for isolated logic - shared AppHost/Aspire fixtures for HTTP, SignalR, SSE, or UI flows - WebApplicationFactory layered over shared Aspire infra when tests need Host DI services, IGrainFactory, or other runtime internals
- Reuse expensive fixtures with
ClassDataSource<Fixture>(Shared = SharedType.PerTestSession)instead of booting distributed infrastructure per test. Fixture reuse does not serialize consumers: keep the fixture concurrency-safe and give each test unique mutable state. - Keep tests and test modules parallel. Do not add
--max-parallel-test-modules 1,TUNIT_MAX_PARALLEL_TESTS=1,[assembly: NotInParallel], a class-wide[NotInParallel], or an equivalent global restriction. - Use keyed
[NotInParallel("collision-domain")]only on the smallest tests that perform destructive changes to the same shared state and can corrupt one another. A shared read-only fixture, expensive startup, module boundary, or vague CI-stability concern is not a reason to limit parallelism. - Run the narrowest useful scope first with
dotnet test ... --treenode-filter "..."on .NET 10. Use the older--separator only when the repository is pinned to an SDK that requires it. - Capture useful failure evidence: host log dumps, focused console output, coverage files, and Playwright screenshots/HTML for UI tests.
- Use
[Test],[Arguments], hooks, and dependencies only when they make the scenario clearer, not because the framework allows it.
Deliver
- TUnit tests that respect source generation and parallel execution
- commands that work in local and CI runs
- framework-specific verification guidance for the repo
- a fixture strategy that matches the actual test scope: logic-only, AppHost/API, Host DI/grains, or Playwright UI
Validate
- the command matches the repo's TUnit runner style
- focused runs use
--treenode-filterrather than VSTest-style--filter - .NET 10 runs pass MTP options directly without a
--separator - no global or assembly-wide single-thread setting has been introduced
- any keyed non-parallel group is limited to tests with a named destructive shared-state collision
- shared distributed fixtures use
SharedType.PerTestSessionor an equivalent reuse pattern - fixture and shared infrastructure are safe for concurrent consumers; mutable data is isolated per test
- built-in TUnit analyzers remain active
- coverage tooling matches Microsoft.Testing.Platform if coverage is enabled
- UI failures capture artifacts and server-side failures expose enough logs to avoid blind reruns
Load References
Related skills
Write, run, or repair .NET tests that use MSTest.
Write, run, or repair .NET tests that use NUnit.