首页 > 解决方案 > 在 AspNetCore 与 TestServer 的集成测试中模拟和解决 Autofac 依赖关系

问题描述

我正在使用 AspNetCore 2.2 以下(更多)文档: https ://docs.microsoft.com/en-us/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

正如其文档所推荐的那样,我使用 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();
            });
}

我现在将 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>();
    }
}

如您所见,我想使用一个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>();
}

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

标签: c#asp.net-coreintegration-testingautofacasp.net-core-testhost

解决方案


使用ConfigureTestContainer向容器构建器注册模拟实现以进行测试

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

Startup.ConfigureContainer这应该避免获得由as添加的实际实现

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

参考默认注册

ConfigureTestContainer在 之后调用,Startup.ConfigureContainer因此最后一次注册模拟将是服务的默认提供者。


推荐阅读