Пересчет и устранение зависимости Autofac в интеграционном тесте в AspNetCore с помощью TestServer
Я использую AspNetCore 2.2. Следую (более) документам здесь: https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2
Я использую Autofac, мой класс запуска имеет следующие методы:
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 хорошим способом.
2 ответа
Зарегистрируйте ложную реализацию в конструкторе контейнеров для теста с использованием ConfigureTestContainer
//...
.ConfigureServices(services => services.AddAutofac())
.ConfigureTestContainer<ContainerBuilder>(builder => {
builder.RegisterType<MockEventStoreManager>().As<IEventStoreManager>();
})
//...
Это должно избежать получения фактической реализации, которая добавляется Startup.ConfigureContainer
как
Если более одного компонента предоставляют одну и ту же услугу, Autofac будет использовать последний зарегистрированный компонент в качестве поставщика этой услуги по умолчанию:
ConfigureTestContainer
вызывается после Startup.ConfigureContainer
поэтому последняя регистрация в макете будет поставщиком услуг по умолчанию.
Добавляя к отличному ответу Nkosi, я хотел бы упомянуть, что
ConfigureTestContainer
не работает с общим хостом, рекомендованным Microsoft для веб-хоста с.NET Core 3.0. Однако есть обходной путь, предложенный Алистером Эвансом из команды Autofac. К сожалению, он полагается на устаревшие
IStartupConfigureContainerFilter
это, вероятно, будет удалено в.NET 5.0.
Это означает, что в настоящее время в.NET 5.0 нет возможности имитировать зависимости, внедренные внешним контейнером DI в интеграционные тесты при использовании универсального хоста.
К счастью, Дэвид Фаулер из команды ASP.NET изучает проблему.
Есть несколько вариантов:
Имейте пользовательскую реализацию вашей зависимости в вашем проекте testserver
Реализовать и вручную зарегистрировать ложную реализацию вашей зависимости с помощью слепка, такого как "Moq".