.NET Core 2.1与3.1中WebApplicationFactory的ConfigureServices调用顺序问询
Is this an intentional change?
Yes, this is an intentional breaking change introduced in ASP.NET Core 3.0 (and maintained in 3.1). The ASP.NET Core team adjusted the service configuration order in WebApplicationFactory to align test setup with how production applications handle service registration.
In .NET Core 2.x, your test's ConfigureServices ran before the app's Startup.ConfigureServices, letting you use TryAdd* methods to "pre-register" services that the app would skip. In 3.x+, the order was reversed: the app's Startup.ConfigureServices runs first, followed by test-specific configuration. This ensures test overrides properly match the real host configuration flow your app uses in production.
Why did this change happen?
The core goal was consistency. In ASP.NET Core 3.x production apps, the host builder's configuration runs after the Startup class's ConfigureServices. WebApplicationFactory was updated to mirror this behavior, so tests now reflect the actual service registration order your app follows live.
Solutions for .NET Core 3.1+
Since the app registers services first, you need to replace existing registrations instead of trying to pre-add them. Here are reliable fixes for your scenario:
1. Replace the service descriptor directly
Modify your test's ConfigureServices to swap the existing IHelloService registration:
protected override void ConfigureServices(IServiceCollection services) { services.Replace(ServiceDescriptor.Singleton<IHelloService, CustomHelloService>()); }
This replaces any existing singleton registration of IHelloService with your custom implementation, regardless of order.
2. Remove all existing registrations first
If you want to ensure no leftover registrations remain, clear all instances of the service type before adding your own:
protected override void ConfigureServices(IServiceCollection services) { services.RemoveAll<IHelloService>(); services.AddSingleton<IHelloService, CustomHelloService>(); }
This is helpful if your app might register multiple implementations of the same interface.
3. Use ConfigureTestServices (recommended approach)
Instead of overriding ConfigureServices in your base class, use ConfigureTestServices directly in the WithWebHostBuilder call. This is the official convention for test-specific service setup in 3.x+:
_factory = new WebApplicationFactory<TStartup>().WithWebHostBuilder(b => { b.UseSolutionRelativeContentRoot(Path.Combine("src", projectRelativePath)) .ConfigureTestServices(services => { services.Replace(ServiceDescriptor.Singleton<IHelloService, CustomHelloService>()); }); });
ConfigureTestServices is designed explicitly for test scenarios, making your code more readable and aligned with ASP.NET Core best practices.
Summary
This is not a bug—it's an intentional change to make test behavior match production. The key adjustment is moving from TryAdd* methods (which relied on running first) to using Replace or RemoveAll + Add* to override existing services.
内容的提问来源于stack exchange,提问作者Lukasz Pyrzyk

