在AspNetCore与TestServer的集成测试中模拟和解决Autofac依赖性

ibe*_*dev 4 c# integration-testing autofac asp.net-core asp.net-core-testhost

我正在使用AspNetCore 2.2,以下(或更多)遵循此处的文档:https ://docs.microsoft.com/zh-cn/aspnet/core/test/integration-tests?view=aspnetcore-2.2

我正在使用Autofac,我的Startup类具有以下方法:

public void ConfigureServices(IServiceCollection services)
public void ConfigureContainer(ContainerBuilder containerBuilder) //this is where things can be registered directly with autofac and runs after ConfigureServices
public void Configure(...) //the method called by runtime
Run Code Online (Sandbox Code Playgroud)

根据其文档的建议,我使用Autofac的方式是 Program.cs like this

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseKestrel()
            .ConfigureServices(services => services.AddAutofac())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .ConfigureAppConfiguration((builderContext, config) =>
            {
                var env = builderContext.HostingEnvironment;
                config
                    .AddJsonFile("appsettings.json", false, true)
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true)
                    .AddEnvironmentVariables();
            });
}
Run Code Online (Sandbox Code Playgroud)

我现在将TestServer用于我的测试项目,我想在其中使用真正的Startup类。但我想修改其中一个依赖项。

我已经看到有可用的一个方法WebHostBuilder.ConfigureTestServices(services => {});

所以我尝试了以下方法:

public abstract class ComponentTestFeature
    : Feature
{
    protected HttpClient HttpClient { get; }

    protected ComponentTestFeature()
    {
        var configuration =
            new ConfigurationBuilder()
                .AddJsonFile("appsettings.Test.json")
                .Build();

        var webHostBuilder =
            new WebHostBuilder()
                .ConfigureServices(services => services.AddAutofac())
                .ConfigureTestServices(services =>
                {
                    services.AddScoped<IEventStoreManager, MockEventStoreManager>();
                })
                .UseConfiguration(configuration)
                .UseStartup<Startup>();

        var server = new TestServer(webHostBuilder);
        HttpClient = server.CreateClient();
        var myService = server.Host.Services.GetRequiredService<IEventStoreManager>();
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我要使用一个MockEventStoreManager实现,IEventStoreManager以便该实现应由容器解决。但是,这不能按预期方式工作,myService解决的是原始实现,真正的实现,而不是模拟的实现。

有人知道我怎么能实现我想要的吗?

更新1:经过更多调查后,我不得不在github AspNetCore上创建一个问题,因为扩展方法是在ConfigureContainer之前执行的,因此我无法真正覆盖autofac依赖项,也无法稍后再获取autofac容器。 https://github.com/aspnet/AspNetCore/issues/6522

更新2:仅供参考,这就是Startup.cs的样子。如您所见,除了mvc外,所有依赖项都在autofac的容器中注册。

public void ConfigureServices(IServiceCollection services)
{
    services
        .AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

// This is where things can be registered directly with autofac and runs after ConfigureServices, so it will override it
public void ConfigureContainer(ContainerBuilder builder)
{
    builder.RegisterType<EventStoreManager>().As<IEventStoreManager>();
}
Run Code Online (Sandbox Code Playgroud)

我想做的是在测试中使用MockEventStoreManager实现IEventStoreManager,因此我需要(从测试项目中)以一种很好的方式覆盖Autofac的注册。

Mat*_*rdt 9

除了 Nkosi 的出色回答之外,我还想提一下,从 .NET Core 3.0 开始,它ConfigureTestContainer不适用于Microsoft 在 Web 主机上推荐的通用主机。不过,Autofac 团队的 Alistair Evans 提出了一种解决方法。不幸的是,它依赖于IStartupConfigureContainerFilter.NET 5.0 中已删除的已弃用的内容。

这意味着当前在 .NET 5.0 中,使用通用主机时,无法在集成测试中模拟外部 DI 容器注入的依赖项。

幸运的是,ASP.NET 团队的 David Fowler正在研究这个问题


Nko*_*osi 7

使用ConfigureTestContainer将模拟的实现注册到容器生成器以进行测试

//...
.ConfigureServices(services => services.AddAutofac())
.ConfigureTestContainer<ContainerBuilder>(builder => {
    builder.RegisterType<MockEventStoreManager>().As<IEventStoreManager>();
})
//...
Run Code Online (Sandbox Code Playgroud)

这应该避免获取由Startup.ConfigureContaineras 添加的实际实现。

如果有多个组件公开同一服务,则Autofac将使用最后注册的组件作为该服务的默认提供程序

参考默认注册

ConfigureTestContainer会在调用之后被调用,Startup.ConfigureContainer因此最后一次向模拟注册将成为该服务的默认提供程序。

  • 对于 Asp .Net Core 3.0+ 或通用托管 `services.AddAutofac()` 不起作用。来自 Autofac 代码文档:“仅适用于 ASP.NET 3.0 之前的托管。这不适用于 ASP.NET CORE 3.0+ 或通用托管。” (3认同)