Unit Testing Strategy
Relevant source files
The following files were used as context for generating this wiki page:
- BuildingBlocks/BuildingBlocks.Tests/TestBase.cs
- BuildingBlocks/BuildingBlocks.Tests/ValidatorTestBase.cs
- Core/Scheduling/Scheduling.Domain.Tests/DomainTests/Patients/PatientTests.cs
- BuildingBlocks/BuildingBlocks.Tests/TransactionBehaviorTests.cs
- BuildingBlocks/BuildingBlocks.Tests/EfCoreUnitOfWorkTests.cs
- Core/Scheduling/Scheduling.Domain.Tests/SchedulingValidatorTestBase.cs
- Core/Scheduling/Scheduling.Domain.Tests/ApplicationTests/HandlerTests/CreatePatientCommandHandlerTests.cs
- claude.md
The project employs a structured, multi-tier testing strategy designed to ensure the reliability of Domain-Driven Design (DDD) patterns and Event-Driven Architecture. The strategy focuses on maintaining high confidence through automated regression prevention, utilizing a hierarchy of test base classes that support different types of tests from pure domain unit tests to complex handler integration tests.
Sources: claude.md:37-45, BuildingBlocks/BuildingBlocks.Tests/TestBase.cs:17-21
Testing Hierarchy and Infrastructure
The test infrastructure is organized into a two-tier hierarchy to handle specific testing concerns. This approach separates lightweight validation logic from heavier integration logic involving database transactions and service providers.
Test Base Classes
| Class | Purpose | Key Dependencies |
|---|---|---|
ValidatorTestBase | Base for unit testing FluentValidation validators. | Moq (Unit of Work), ServiceCollection |
TestBase<TContext> | Base for integration tests requiring a real database and MediatR. | SQLite In-Memory, EF Core, MediatR |
SchedulingValidatorTestBase | Bounded context specific base for Scheduling validators. | Mocked IRepository<Patient> |
Sources: claude.md:40-45, BuildingBlocks/BuildingBlocks.Tests/TestBase.cs:17-100, Core/Scheduling/Scheduling.Domain.Tests/SchedulingValidatorTestBase.cs:13-25
Test Isolation and Database Strategy
Integration tests utilize SQLite in-memory databases to ensure speed and consistency. Every test run is wrapped in a transaction that is explicitly rolled back during TestCleanup, preventing state leakage between tests without the overhead of recreating the database schema for every method.
Sources: BuildingBlocks/BuildingBlocks.Tests/TestBase.cs:42-83, claude.md:42
The diagram shows the lifecycle of an integration test, emphasizing the transactional rollback strategy.
Sources: BuildingBlocks/BuildingBlocks.Tests/TestBase.cs:42-83
Domain Entity Testing
Domain unit tests focus on the business logic encapsulated within Aggregate Roots and Entities. These tests verify state transitions, domain event raising, and encapsulation rules without any infrastructure dependencies.
- State Transitions: Verifying that methods like
Suspend()orActivate()change the internal status correctly. - Domain Events: Ensuring that significant business actions (e.g.,
Delete()) raise the expected domain events for internal processing. - Immutable Value Objects: Testing that value objects and entities maintain valid states through constructor or factory method logic.
Sources: Core/Scheduling/Scheduling.Domain.Tests/DomainTests/Patients/PatientTests.cs:10-99, claude.md:95-102
Command and Validator Testing
The project uses FluentValidation for command and query validation. Tests are designed to verify both standard property validation (e.g., required fields, email formats) and complex business rules (e.g., entity existence checks or role-based authorization).
Validator Unit Tests
Validators are tested using ValidatorTestBase, which provides a mocked IUnitOfWork. This allows the tests to simulate database checks (like ExistsAsync) without a real database.
This sequence illustrates how validators interact with mocked infrastructure to verify business constraints.
Sources: Core/Scheduling/Scheduling.Domain.Tests/SchedulingValidatorTestBase.cs:13-40, BuildingBlocks/BuildingBlocks.Tests/ValidatorTestBase.cs
Infrastructure and Pipeline Testing
Specific tests are dedicated to the "Building Blocks" of the system, ensuring that cross-cutting concerns like transactions and the Unit of Work pattern function correctly across different scenarios.
Transactional Behaviors
TransactionBehaviorTests verify the MediatR pipeline logic. This ensures that:
- Commands automatically trigger a transaction.
- Queries do not start transactions (performance optimization).
- Exceptions trigger a rollback, while
ValidationExceptiontriggers a commit (to persist validator side-effects if applicable).
Sources: BuildingBlocks/BuildingBlocks.Tests/TransactionBehaviorTests.cs:25-100
Unit of Work and Nested Transactions
EfCoreUnitOfWorkTests focus on the complexity of nested transactions. The system uses a depth counter to ensure that only the outermost scope commits the transaction, while inner scopes participate in the existing one.
The diagram represents the nested transaction logic where the commit only occurs when the depth reaches zero.
Sources: BuildingBlocks/BuildingBlocks.Tests/EfCoreUnitOfWorkTests.cs:110-150
Testing Tools and Libraries
The project standardizes on a specific set of tools to ensure readable and maintainable tests:
- MSTest: The primary test runner and framework.
- Shouldly: Used for expressive and readable assertions (e.g.,
response.Success.ShouldBeTrue()). - Moq: The standard framework for creating mocks and verifying interactions.
- NBuilder: Used for generating test data and complex object graphs fluently.
Sources: claude.md:120-124, Core/Scheduling/Scheduling.Domain.Tests/ApplicationTests/HandlerTests/CreatePatientCommandHandlerTests.cs:18-35
Summary
The Unit Testing Strategy in this project balances unit and integration testing. Domain logic is validated through lightweight unit tests, while complex application workflows—including database persistence and transactional integrity—are verified using a robust integration test base. By utilizing transactional rollbacks and in-memory databases, the strategy provides a high degree of isolation and speed.
Sources: claude.md:37-45, BuildingBlocks/BuildingBlocks.Tests/TestBase.cs:17-40