Most mediator libraries ship with unit tests that verify correctness under ideal conditions — single-threaded, synchronous, no failures. DSoftStudio.Mediator is validated under conditions that reflect real production systems: concurrency, failure, chaos, and resource pressure.
This mediator is validated under concurrency, failure, and chaos — not just benchmarks.
This page documents the enterprise integration test suite, what each category measures, and why it matters.
These tests are designed to answer one question:
What happens when the system is under real pressure?
Not:
But:
48 tests across 14 categories, covering scenarios that most mediator libraries do not test.
| Category | Tests | What it validates |
|---|---|---|
| Multi-project discovery | 4 | Cross-assembly handler resolution with source generators |
| Dependency injection lifetimes | 4 | Scoped/Transient/Singleton correctness through the pipeline |
| Deep pipeline (6 behaviors) | 4 | Behavior ordering, exception propagation, retry, exception handlers |
| Concurrency | 3 | 2000+ parallel Send/Publish with result verification |
| Native AOT safety | 3 | PrecompilePipelines removes open-generics, idempotent |
| Expression tree safety | 2 | Moq/NSubstitute compatibility — no interceptor rewrite |
| Complex generics | 5 | Nullable reference/value types, stream with value-type response |
| Runtime vs compile-time dispatch | 4 | Send(object) matches typed Send<T>, 500 parallel |
| Background service patterns | 3 | Scoped mediator in hosted service loops, cancellation |
| Stress testing | 3 | 5000 sequential, 100 parallel streams, mixed workload |
| Failure injection | 4 | Cancellation mid-pipeline, retry recovery, intermittent failures |
| Allocation regression | 3 | Per-request allocation budget, disposal verification, memory stability |
| Timeout and deadlock detection | 3 | Slow behavior chains, nested Send, thread pool starvation |
| Chaos scenarios | 3 | Random delays + random exceptions + high concurrency |
| Production risk | Test category that covers it |
|---|---|
| Race conditions under load | Concurrency, Stress, Chaos |
| Memory leaks or GC pressure | Allocation regression |
| Flaky handler failures | Failure injection |
| Deadlocks from nested dispatch | Timeout and deadlock detection |
| DI lifetime violations | Dependency injection lifetimes |
| AOT deployment failures | Native AOT safety |
| Test double incompatibility | Expression tree safety |
| Pipeline behavior ordering bugs | Deep pipeline |
| Lost messages under concurrency | Concurrency, Background service patterns |
Test suite analysis based on public GitHub repositories (verified June 2025):
| Library | Repository | Unit tests | Pipeline tests | Concurrency tests | Failure injection | Chaos tests |
|---|---|---|---|---|---|---|
| DSoftStudio.Mediator | DSoftStudio/Mediator | ✅ | ✅ | ✅ (2000+) | ✅ | ✅ |
| Mediator (SG) 3.0 | martinothamar/Mediator | ✅ | ✅ | ❌ | ❌ | ❌ |
| DispatchR 2.1 | hasanxdev/DispatchR | ✅ | Basic | ❌ | ❌ | ❌ |
| MediatR 14.1 | LuckyPennySoftware/MediatR | ✅ | ✅ | ❌ | ❌ | ❌ |
Validates that source-generated handler discovery works across assembly boundaries.
| Test | What it verifies |
|---|---|
AllHandlers_DiscoveredAndResolvable_AcrossAssembly |
ValidateMediatorHandlers() resolves every handler from DI, including cross-assembly |
Send_HandlerFromDifferentNamespace_ReturnsCorrectResult |
Handler in Infrastructure/ namespace returns correct result |
Publish_NotificationHandlerFromDifferentNamespace_DoesNotThrow |
Notification handler in different namespace is discovered |
CreateStream_HandlerFromDifferentNamespace_YieldsAllItems |
Stream handler in different namespace yields all items |
Production impact: Multi-project solutions where domain handlers and API handlers live in separate assemblies.
Validates Scoped, Transient, and Singleton lifetimes work correctly through the mediator pipeline.
| Test | What it verifies |
|---|---|
SameScope_ScopedDependency_SameInstance |
Same scope → same ScopedCorrelation.Id across multiple Send calls |
DifferentScopes_ScopedDependency_DifferentInstances |
Different scopes → different scoped instances, same singleton |
TransientDependency_DifferentAcrossScopes |
Different scopes → different TransientStamp.Id |
SingletonCounter_SharedAcrossParallelScopes |
50 parallel scopes share the same SingletonCounter, no lost increments |
Production impact: ASP.NET Core request scopes, EF Core DbContext lifetime, per-request correlation IDs.
Validates 6 behaviors in correct order with exception handling.
| Test | What it verifies |
|---|---|
SixBehaviors_ExecuteInCorrectNestingOrder |
Logging → Validation → Auth → Metrics → Retry → Caching — all fire with correct enter/exit pairing |
DeepPipeline_HandlerThrows_ExceptionPropagates |
Exception propagates through all 6 behaviors; retry behavior retries once |
DeepPipeline_WithExceptionHandler_FallbackResultReturned |
IRequestExceptionHandler intercepts and provides fallback result |
DeepPipeline_MixedSyncAsync_CompletesCorrectly |
10 consecutive sends through 6-behavior pipeline — stability |
Production impact: Enterprise pipelines with logging, validation, authorization, metrics, retry, and caching behaviors stacked together.
Validates correctness under high parallelism with no race conditions.
| Test | What it verifies |
|---|---|
Send_2000Parallel_AllResultsCorrect |
2000 parallel Send calls — every result matches seed × 2 |
Publish_1000Parallel_AllNotificationsReceived |
1000 parallel Publish calls — all notifications received, no duplicates |
MixedSendAndPublish_1000Parallel_NoInterference |
500 parallel Send + 500 parallel Publish — no cross-contamination |
Production impact: High-throughput APIs handling thousands of concurrent requests.
Validates that PrecompilePipelines() produces AOT-compatible service registrations.
| Test | What it verifies |
|---|---|
PrecompilePipelines_RemovesAllOpenGenericBehaviors |
Open-generic IPipelineBehavior<,> replaced with closed-generic — no MakeGenericType at runtime |
PrecompiledBehaviors_FireCorrectly_ForValueAndReferenceTypes |
Behaviors fire for both int and Unit response types after precompilation |
PrecompilePipelines_CalledTwice_StillWorksCorrectly |
Idempotent — calling twice doesn’t corrupt registrations |
Production impact: Native AOT deployments, trimmed applications, serverless cold start optimization.
Validates that Moq/NSubstitute expression trees don’t trigger interceptor rewriting.
| Test | What it verifies |
|---|---|
ExpressionTree_WithSendCall_DoesNotCrash |
Expression<Func<ISender, ValueTask<int>>> compiles without CS8652 |
ExpressionTree_WithPublishCall_DoesNotCrash |
Expression<Func<IPublisher, Task>> compiles without CS8652 |
Production impact: Test projects using Moq Setup(() => sender.Send(...)) pattern.
Validates nullable types and complex generic constraints through the full pipeline.
| Test | What it verifies |
|---|---|
NullableReferenceType_NonNullResult |
IRequest<string?> returns non-null correctly |
NullableReferenceType_NullResult |
IRequest<string?> returns null correctly |
NullableValueType_NonNullResult |
IRequest<int?> returns 42 correctly |
NullableValueType_NullResult |
IRequest<int?> returns null correctly |
Stream_WithValueTypeResponse_YieldsCorrectItems |
IStreamRequest<int> yields correct sequence |
Production impact: Domain models with nullable responses, optional query results.
Validates Send(object) runtime dispatch matches typed Send<TRequest, TResponse>.
| Test | What it verifies |
|---|---|
TypedSend_vs_ObjectSend_SameResult |
Both dispatch paths return identical results |
ObjectSend_UnitResponse_ReturnsUnit |
Send(object) with void request returns Unit.Value |
ObjectSend_UnregisteredType_ThrowsInvalidOperationException |
Unregistered type throws — no silent default |
ObjectSend_500Parallel_AllCorrect |
500 parallel Send(object) calls — no race conditions in FrozenDictionary |
Production impact: Generic middleware, message routing, dynamic dispatch scenarios.
Validates mediator usage from IHostedService / background worker patterns.
| Test | What it verifies |
|---|---|
BackgroundWorker_ProcessesJobsInScopes |
20 sequential jobs in individual scopes — correct DI pattern |
BackgroundWorker_CancellationToken_Respected |
Cancellation token propagates through the pipeline |
MultipleWorkers_ConcurrentScopes_NoLostIncrements |
10 workers × 50 jobs — 500 total, singleton counter matches |
Production impact: Background job processors, queue consumers, scheduled tasks.
Sustained load testing with consistent result verification.
| Test | What it verifies |
|---|---|
SequentialStress_5000Requests_AllCorrect |
5000 sequential requests — no state corruption, consistent results |
ParallelStreams_100Consumers_AllItemsReceived |
100 parallel stream consumers × 100 items — 10,000 total, no lost items |
MixedWorkload_SendPublishStream_NoInterference |
200 parallel iterations of Send + Publish + Stream — no cross-contamination |
Production impact: Sustained API load, batch processing, event-driven architectures.
Validates behavior under adverse conditions.
| Test | What it verifies |
|---|---|
CancellationDuringHandler_ThrowsOperationCanceledException |
5s handler cancelled after 50ms — OperationCanceledException propagates |
RetryBehavior_RecoversFlakyHandler |
Handler fails once, retry behavior recovers on second attempt |
RetryExhausted_ExceptionPropagates |
Handler always fails — retry exhausted, exception reaches caller |
IntermittentFailures_ParallelSend_NoCrossContamination |
500 parallel sends, ~50% fail — each result is success or exception, never wrong value |
Production impact: Transient database failures, network timeouts, circuit breaker patterns.
Validates memory behavior under sustained load.
| Test | What it verifies |
|---|---|
SequentialRequests_AllocationPerRequestBounded |
10,000 requests — per-request allocation < 10 KB (validates no cumulative leak) |
ScopedDisposal_DisposableResourcesReleased |
ScopedCorrelation.Disposed == true after scope ends |
ParallelScopes_StableMemory |
5 waves × 100 parallel scopes — memory growth < 10 MB after GC |
Production impact: Long-running services, memory-constrained environments, GC pressure monitoring.
Validates async pipeline doesn’t deadlock under contention.
| Test | What it verifies |
|---|---|
SlowBehaviorChain_CompletesWithinTimeout |
3 slow behaviors (10ms each) + handler — completes within 5s timeout |
NestedSendInsideBehavior_NoDeadlock |
Behavior sends Ping inside NestedOuterPing pipeline — no deadlock |
ParallelSend_WithSlowBehaviors_CompletesWithinTimeout |
200 parallel sends with slow behaviors — all complete within 30s |
Production impact: Complex pipelines where behaviors invoke the mediator, thread pool exhaustion under load.
Random delays + random exceptions + high concurrency.
| Test | What it verifies |
|---|---|
ChaosMode_1000Parallel_MostSucceed |
1000 parallel sends, 10% failure rate — ≥70% succeed, total = 1000 |
MixedWorkloadWithChaos_NoCrossContamination |
Send (with chaos) + Publish (no chaos) — notifications always arrive |
HighFailureRate_WithExceptionHandler_GracefulDegradation |
50% failure rate + exception handler — all 500 requests return (success or fallback), zero exceptions to caller |
Production impact: Unreliable downstream services, partial outages, graceful degradation patterns.
dotnet test tests/DSoftStudio.Mediator.Tests --filter "FullyQualifiedName~Integration.Enterprise"
Or run the full suite:
dotnet test tests/DSoftStudio.Mediator.Tests
All 48 enterprise integration tests run as part of the CI pipeline on every push.
Run the tests. Read the results. Decide.