Как переопределить регистрацию 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 изучает проблему.

Другие вопросы по тегам