Как вы тестируете всю коллекцию услуг подряд?

Давным-давно…

Как вы все знаете, когда мы начнем разрабатывать приложение ASP.NET Core, у нас будет 1, 2 или 3 службы, которые быстро создадут 5 или 10 модульных тестов, чтобы проверить, все ли службы зарегистрированы ... А затем, Commit, Push, Слияние, готово. Легкий лимонный отжим!

Но тогда на следующий день вы, скорее всего, создадите еще один модульный тест, на следующей неделе, вероятно, потребуется удалить два из них и создать третий и так далее. С другой стороны, в то же время вы замечаете, что ваш приятель «забывает» создать модульный тест для некоторой службы, которую он создал. Что это значит? Да, все верно, впереди еще много проблем. 😓

Что ж, я знаю, что у вас наверняка есть интеграционные тесты и / или автоматические тесты, которые обнаружат любую ошибку. Правильно? Хм, нет. Я почти уверен, что у большинства из вас нет великолепной пирамиды тестирования. На самом деле, я бы сказал, что у нас есть кусок швейцарского вонючего сыра с множеством дырок 🙄, что на самом деле довольно неплохо! (сыр, а не покрытие 🧀😋).

Я работаю над приложением, которое имеет ок. 700 сервисов, сотни конечных точек, несколько запросов на слияние, +100 разработчиков, ежедневно пишущих код, низкое покрытие кода, нулевые тесты интеграции и нулевые автоматизированные тесты 🔥😈. Результат? Сливается с плохо зарегистрированными зависимостями.

Однажды я решил разработать модульный тест для проверки всей коллекции сервисов. Вы можете угадать заявленный результат? ПОТЕРПЕТЬ ПОРАЖЕНИЕ!

Как автоматизировать, шаг за шагом

Во-первых, нам нужна ServiceCollection со всем нашим списком зависимостей. Вы должны включить все, что можете. В этом примере я включил все зависимости ASP.NET Core и зависимости моего приложения из файла MyAppStartup. Затем нам нужно создать приложение WebHost для извлечения ServiceCollection.

Обратите внимание, что theMyAppStartup - это настоящее запускаемое приложение. EmptyStartup - это пустой запуск, необходимый только для WebHostBuilder, чтобы зарегистрировать и запустить приложение. Ключ состоит в том, чтобы создать классMyAppStartup в методе ConfigureAppConfiguration и использовать его в методе ConfigureServices для извлечения всего списка из вашей коллекции служб после настройки служб.

❗ ️Инициализировать IConfiguration файлом JSON не требуется. Это просто пример, если вы хотите принимать решения о загрузке коллекции сервисов на основе настроек приложения. Не забывайте об этих случаях и убедитесь, что вы создали разные сценарии для Production, Staging иDevelopment сред.

Давай протестируем!

К счастью, пакет Microsoft.Extensions.DependencyInjection имеет расширение BuildServiceProvider отIServiceCollection со всеми конфигурациями, которые могут выполнять тяжелую работу. Одна из этих опций - theValidateOnBuild, которая глубоко проверяет свои зависимости, и если что-то пойдет не так, построитель выдаст исключение. Фактически, он выдает AggregateException, который объединяет исключения для каждой недопустимой службы.

Как добавить контроллеры и многое другое

Помимо контроллеров и компонентов представления, есть еще объекты и службы, которые не были зарегистрированы. Это зависит от конфигурации приложения (MVC, Endpoints, RazorPages и т. Д.)

Если вам нужно добавить больше объектов в коллекцию сервисов и проверить свои зависимости от сборки поставщика услуг, используйте отражение и LINQ для поиска, фильтрации и добавления, как вы можете видеть ниже.

Помимо конструктора, действия параметров можно связать со службами с помощью attribute[FromServices]. Приведенный ниже код можно использовать для поиска этих атрибутов и регистрации их как зависимости некоего объекта. Класс DependencyService<T> - это подделка, зависящая только от регистрации в коллекции сервисов.

Как добавить промежуточное ПО

Промежуточное ПО может иметь зависимости, которые должны быть разрешены в конструкторе и методе InvokeAsync. Используя ту же стратегию, что и в контроллерах, вы можете искать метод Invoke в классах промежуточного программного обеспечения. Здесь я рассматриваю класс промежуточного программного обеспечения как объект, имя которого содержит слово промежуточное программное обеспечение или подклассы интерфейса IMiddleware.

Задача: обнаружение зависимостей от вызовов HttpContext.RequestServices

Я попытался использовать отражение и анализатор Roslyn, чтобы обнаружить HttpContext.RequestServices вызовов, но ничего не получилось с достаточной уверенностью для производственного модульного теста. Думаю, эта версия - хороший MVP. Если у вас есть предложения, свяжитесь со мной, и мы обязательно сможем работать вместе. Далее я изучу и постараюсь об этом написать. 🙂

Исход

Вы можете использовать приведенный ниже модульный тест для проверки всех зависимостей ваших сервисов, но я должен вам посоветовать: он не решит всех ваших проблем с регистрацией сервисов, хотя и предотвратит большинство из них. Используйте свое воображение и навыки развития, чтобы добавить больше угловых случаев. Сэкономьте время, написав меньше модульных тестов, и вы с большей уверенностью перейдете к производственной среде. Я действительно верю, что вы (я) станете лучшим программистом, иначе вы бы не читали эти последние слова.