In this lesson, you will learn how to configure your Dependency Injection (DI) containers for testing so they mirror production behavior while allowing precise control over specific component swaps.
The Testing DI Dilemma
When testing at the integration level, you often want to keep 90% of your production DI configuration to ensure the application graph is wired correctly. However, you need to replace "noisy" dependencies—like payment gateways, email services, or external APIs—with test doubles (mocks or fakes) without manually re-registering every single service.
The goal is to maintain the production-like wiring while using a "Descriptor Override" pattern to swap only what matters for the test.
Implementing Descriptor Overrides
Instead of creating a separate container, use the IServiceCollection to clear and replace specific descriptors. Since IServiceCollection is an IList<ServiceDescriptor>, you can easily locate an existing registration and remove it before injecting your test-specific implementation.
public void ConfigureTestServices(IServiceCollection services)
{
// 1. Locate the production service
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(IPaymentGateway));
// 2. Remove it safely
if (descriptor != null)
{
services.Remove(descriptor);
}
// 3. Register the test version
services.AddScoped<IPaymentGateway, MockPaymentGateway>();
}
Why This Mimics Production
By removing and replacing specific descriptors, you ensure that the application's startup logic (like health checks, logging, and infrastructure wiring) remains identical to production. If you simply initialized a new ServiceCollection for your tests, you would inevitably miss critical registrations, leading to "false negatives" where tests fail because of missing DI configuration rather than faulty logic.
By using this approach within your WebApplicationFactory (or equivalent test infrastructure), you guarantee that the dependency graph is valid, while the specific behaviors you are testing remain isolated and deterministic. This creates a "controlled production" environment where the infrastructure is real, but the externalities are fully under your command.
Key Takeaway: Leverage the DI container as an ordered list to surgically replace production dependencies, ensuring the integrity of your global service graph while isolating the specific component under test.