- Behavioral parity between source-generated and reflection modes is mandatory
- Run snapshot tests when changing:
- Source generator output:
dotnet test TUnit.Core.SourceGenerator.Tests - Public APIs:
dotnet test TUnit.PublicAPI
- Source generator output:
- Accept snapshots: Rename
.received.txt→.verified.txtand commit - Use Microsoft.Testing.Platform, never VSTest
- Performance is paramount - this framework may be used by millions
- Source Generated: Compile-time generation for performance
- Reflection: Runtime for dynamic scenarios
- Both must produce identical behavior
- TUnit.Core: Abstractions and interfaces
- TUnit.Engine: Test discovery and execution
- TUnit.Core.SourceGenerator: Compile-time generation
- TUnit.Assertions: Fluent assertions
- TUnit.Analyzers: Compile-time validation
- Collection initializers:
List<string> list = [] - Pattern matching, records, file-scoped namespaces
varfor obvious typesValueTaskfor potentially synchronous operations
if (condition)
{
DoSomething(); // Always use braces
}- PascalCase public, _camelCase private fields
- Expression-bodied members for simple logic
- Meaningful names over comments
- Minimize allocations in hot paths
- Object pooling for frequent allocations
- Cache reflection results
- Profile discovery and execution paths
- Implement in both execution modes
- Add analyzer rules if applicable
- Write comprehensive tests
- Verify performance impact
- Update documentation
- Write failing test
- Fix in all affected modes
- Verify no performance regression
- Both modes tested
- Source generator snapshots accepted (if changed)
- Public API snapshots accepted (if changed)
- Performance considered
- No breaking changes
# Run all tests
dotnet test
# Source generator tests
dotnet test TUnit.Core.SourceGenerator.Tests
# Public API tests
dotnet test TUnit.PublicAPI
# Accept snapshots (Windows)
for %f in (*.received.txt) do move /Y "%f" "%~nf.verified.txt"
# Accept snapshots (Linux/macOS)
for file in *.received.txt; do mv "$file" "${file%.received.txt}.verified.txt"; done
# Run specific test
dotnet test -- --treenode-filter "/Assembly/Namespace/ClassName/TestName"- Error Handling: Specific exceptions with context
- Async:
CancellationTokensupport throughout - Reflection: AOT-friendly with
[UnconditionalSuppressMessage] - Threading: Ensure concurrent test safety
- Disposal: Proper resource cleanup
- Unit tests for components
- Integration for cross-component
- Performance benchmarks for critical paths
- Analyzer tests for compile-time rules
- Snapshot tests for generator and API surface
- .NET Standard 2.0, .NET 6, 8, 9+
- AOT and trimming support
- Various project configurations
- Mode inconsistency between source-gen and reflection
- Performance regressions in discovery/execution
- AOT/trimming issues with reflection
- Thread safety in concurrent execution
- Resource leaks from improper disposal
- Forgetting to accept intentional snapshot changes
Every change must maintain TUnit's goals: fast, modern, reliable, and enjoyable to use.