使用 TestServer 在 AspNetCore 中的集成测试中模拟和解决 Autofac 依赖关系
Mocking and resolving Autofac dependency in integration test in AspNetCore with TestServer
我正在使用 AspNetCore 2.2
关注(更多)这里的文档:
https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2
我正在使用 Autofac,我的 Startup class 有以下方法:
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 class。但是我想修改其中一个依赖。
我看到 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>();
}
}
如您所见,我想对 IEventStoreManager
使用 MockEventStoreManager
实现,因此这是应该由容器解析的实现。然而,这并没有按预期工作,并且 myService resolved 是原始实现,真实的,而不是模拟的。
有人知道我怎样才能实现我想要的吗?
更新 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 的注册。
有几个选项:
在您的测试服务器中自定义实现您的依赖项
项目
使用像 "Moq"
这样的金块实现并手动注册您的依赖项的模拟实现
向容器构建器注册模拟实现以进行测试
//...
.ConfigureServices(services => services.AddAutofac())
.ConfigureTestContainer<ContainerBuilder>(builder => {
builder.RegisterType<MockEventStoreManager>().As<IEventStoreManager>();
})
//...
这应该避免获得 Startup.ConfigureContainer
添加的实际实现,如
If more than one component exposes the same service, Autofac will use the last registered component as the default provider of that service:
Reference Default Registrations
ConfigureTestContainer
在 Startup.ConfigureContainer
之后调用,因此最后一次注册模拟将是服务的默认提供者。
添加到 Nkosi 的优秀 , I'd like to mention 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。不幸的是,它依赖于已在 .NET 5.0 中删除的已弃用 IStartupConfigureContainerFilter
。
这意味着当前在 .NET 5.0 中,当使用通用主机时,无法在集成测试中模拟由外部 DI 容器注入的依赖项。
幸运的是,来自 ASP.NET 团队的 David Fowler is looking into the issue。
我正在使用 AspNetCore 2.2 关注(更多)这里的文档: https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2
我正在使用 Autofac,我的 Startup class 有以下方法:
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 class。但是我想修改其中一个依赖。
我看到 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>();
}
}
如您所见,我想对 IEventStoreManager
使用 MockEventStoreManager
实现,因此这是应该由容器解析的实现。然而,这并没有按预期工作,并且 myService resolved 是原始实现,真实的,而不是模拟的。
有人知道我怎样才能实现我想要的吗?
更新 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 的注册。
有几个选项:
在您的测试服务器中自定义实现您的依赖项 项目
使用像 "Moq"
这样的金块实现并手动注册您的依赖项的模拟实现
//...
.ConfigureServices(services => services.AddAutofac())
.ConfigureTestContainer<ContainerBuilder>(builder => {
builder.RegisterType<MockEventStoreManager>().As<IEventStoreManager>();
})
//...
这应该避免获得 Startup.ConfigureContainer
添加的实际实现,如
If more than one component exposes the same service, Autofac will use the last registered component as the default provider of that service:
Reference Default Registrations
ConfigureTestContainer
在 Startup.ConfigureContainer
之后调用,因此最后一次注册模拟将是服务的默认提供者。
添加到 Nkosi 的优秀 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。不幸的是,它依赖于已在 .NET 5.0 中删除的已弃用 IStartupConfigureContainerFilter
。
这意味着当前在 .NET 5.0 中,当使用通用主机时,无法在集成测试中模拟由外部 DI 容器注入的依赖项。
幸运的是,来自 ASP.NET 团队的 David Fowler is looking into the issue。