Как переопределить регистрацию DI из другого контейнера в интеграционном тесте ASP.NET Core
У меня есть следующая регистрация в файле core.cs ядра asp.net:
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<UserService>().As<IUserService>();
}
Это для настройки контейнера Autofac. И у меня есть еще один проект тестирования интеграции, где у меня есть класс CustomWebApplicationFactory, и я пытаюсь заменить реализацию интерфейса IUserService.
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services.AddSingleton<IUserService, TestUsersService>();
});
}
Кажется, это не работает, когда я отлаживаю тестовый проект, и реализация IUserService все еще остается UserService.
Я попытался зарегистрировать UserService непосредственно в методе Startup.ConfigureServices с помощью встроенной в ASP.NET Core IServiceCollection, и она работала при отладке:
services.AddSingleton<IUserService, UserService>();
Итак, как я могу исправить проблему, когда я использую Autofac в качестве контейнера IoC, и проект тестирования интеграции будет работать должным образом, как я ожидаю?
1 ответ
Вы, вероятно, столкнулись с проблемой порядка операций. В общем, последний в победах. Это касается Autofac и базового контейнера Microsoft DI.
Предполагая, что вы прочитали документы по интеграции с Autofac ASP.NET Core, вы увидите, что когда ConfigureContainer
на месте порядок операций примерно:
- WebHost конкретные ConfigureServices
- Класс запуска ConfigureServices
- Класс запуска ConfigureContainer
При добавлении ConfigureTestServices на месте это выглядит (хотя я не прошел), что он запускается после WebHost и класса запуска ConfigureServices... но он все еще работает до ConfigureContainer.
Это было бы достаточно просто проверить - создать интерфейс службы с тремя различными реализациями. Зарегистрируйте разные реализации на каждом уровне. Разрешите интерфейс в контроллере. Какой вы получили? Это был последний, чтобы бежать. Теперь удалите эту регистрацию из приложения и попробуйте снова. Какой следующий вы получите? Это со второго до последнего. И так далее.
Autofac берет предварительно построенный IServicesCollection
и циклически перебирает его, добавляя его в собственный контейнер Autofac. Как только это произошло, не имеет значения, измените ли вы коллекцию. Autofac не контролирует порядок выполнения механизма запуска в ASP.NET Core; он просто знает, что ASP.NET Core говорит: "Вот последняя коллекция сервисов, которую можно продолжить и импортировать!" Если это не происходит на правильной стадии, вам придется сделать одну из двух вещей:
- Переместите регистрацию, которую нужно переопределить, из
ConfigureContainer
и в один изConfigureServices
методы, используя язык регистрации Microsoft вместо родного Autofac. - Выполните переопределение другим способом, например, используя
ASPNETCORE_ENVIRONMENT
Установка изTest
и предоставлениеConfigureTestContainer
метод. (Примеры методов регистрации для конкретной среды приведены в документации.)
При использовании ContainerBuilder, как это:
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<UserService>().As<IUserService>();
}
Вы должны использовать ConfigureTestContainer вместо ConfigureTestServices следующим образом:
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestContainer<ContainerBuilder>(containerBuilder =>
{
containerBuilder.RegisterType<TestUsersService>().As<IUserService>();
});
}
Это выполняется после вызова ConfigureContainer и корректно переопределит IUserService
с TestUsersService
Для тех, кто пришел из Google, я хотел бы добавить к отличному ответу Майкла, что
ConfigureTestContainer
не работает с общим хостом, рекомендованным Microsoft для веб-хоста с.NET Core 3.0. Однако есть обходной путь, предложенный Алистером Эвансом из команды Autofac. К сожалению, он полагается на устаревшие
IStartupConfigureContainerFilter
это, вероятно, будет удалено в.NET 5.0.
Это означает, что в настоящее время в.NET 5.0 нет возможности имитировать зависимости, внедренные внешним контейнером DI в интеграционные тесты при использовании универсального хоста.
К счастью, Дэвид Фаулер из команды ASP.NET изучает проблему.