Как вы тестируете всю коллекцию услуг подряд?
Давным-давно…
Как вы все знаете, когда мы начнем разрабатывать приложение 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. Если у вас есть предложения, свяжитесь со мной, и мы обязательно сможем работать вместе. Далее я изучу и постараюсь об этом написать. 🙂
Исход
Вы можете использовать приведенный ниже модульный тест для проверки всех зависимостей ваших сервисов, но я должен вам посоветовать: он не решит всех ваших проблем с регистрацией сервисов, хотя и предотвратит большинство из них. Используйте свое воображение и навыки развития, чтобы добавить больше угловых случаев. Сэкономьте время, написав меньше модульных тестов, и вы с большей уверенностью перейдете к производственной среде. Я действительно верю, что вы (я) станете лучшим программистом, иначе вы бы не читали эти последние слова.