简体   繁体   中英

How to override DI registration from other container in ASP.NET Core integration test

I have the following registration in asp.net core startup.cs file:

    public void ConfigureContainer(ContainerBuilder builder)
    {
       builder.RegisterType<UserService>().As<IUserService>();
    }

This is to configure the Autofac container. And I have another integration test project where I have a CustomWebApplicationFactory class and I'm trying to replace the implementation of IUserService interface.

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureTestServices(services =>
        {
            services.AddSingleton<IUserService, TestUsersService>();
        });
    }

It seems not work when I debug the test project and the implementation of IUserService is still UserService.

I tried registering UserService directly in Startup.ConfigureServices method with ASP.NET Core built-in IServiceCollection and it worked when debugging:

services.AddSingleton<IUserService, UserService>();

So, how can I fix the problem when I use Autofac as the IoC container and the integration test project will work properly as I expect?

You are likely running into an order of operations problem. In general, last in wins. That goes for Autofac and for the base Microsoft DI container.

Assuming you've read through the docs on Autofac ASP.NET Core integration you'll see that when ConfigureContainer is in place the order of operations is roughly:

  • WebHost specific ConfigureServices
  • Startup class ConfigureServices
  • Startup class ConfigureContainer

When adding ConfigureTestServices in place, it looks like (though I haven't stepped through) it runs after the WebHost and Startup class ConfigureServices... but it's still running before ConfigureContainer.

This would be easy enough to test - create a service interface with three different implementations. Register a different implementation at each level. Resolve the interface in a controller. Which one did you get? That was the last one to run. Now remove that registration from the app and try again. What's the next one you get? That's the second to the last one. And so on.

Autofac takes a pre-built IServicesCollection and cycles through it, adding that to the native Autofac container. Once that's happened, it doesn't matter if you modify the collection. Autofac has no control over the order of the execution of the startup mechanism in ASP.NET Core; it just knows that ASP.NET Core says, "Here's the final service collection to go ahead and import!" If that's not happening at the right stage, you'll have to do one of two things:

  • Move the registration you need to override out of ConfigureContainer and into one of the ConfigureServices methods, using the Microsoft registration language instead of native Autofac.
  • Perform the override some other way like using an ASPNETCORE_ENVIRONMENT setting of Test and providing a ConfigureTestContainer method. (Examples of environment specific registration methods are in the docs .)

When using ContainerBuilder like this:

    public void ConfigureContainer(ContainerBuilder builder)
    {
        builder.RegisterType<UserService>().As<IUserService>();
    }

You must use ConfigureTestContainer rather than ConfigureTestServices as follows:

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureTestContainer<ContainerBuilder>(containerBuilder =>
            {
                containerBuilder.RegisterType<TestUsersService>().As<IUserService>();
            });
    }

This is executed after the call to ConfigureContainer and will correctly override IUserService with TestUsersService

For the ones coming from google I'd like to add to Michael's excellent answer that ConfigureTestContainer does not work with the generic host recommended over the web host by Microsoft as of .NET Core 3.0. There is however a workaround proposed by Alistair Evans from the Autofac team . Unfortunately, it relies on the deprecated IStartupConfigureContainerFilter that will probably be removed in .NET 5.0.

This means that currently in .NET 5.0 there is no way to mock dependencies injected by an external DI container in integration tests when using the generic host.

Luckily, David Fowler from the ASP.NET team is looking into the issue .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM