Инфраструктура ASP.NET в обработчиках MediatR
Я предпочитаю, чтобы мои обработчики освобождались от инфраструктуры ASP.NET, которую очень сложно проверить (да, даже в ASP.NET Core). Но иногда это происходит, и у вас есть зависимость, такая как UserManager (я хотел бы знать, что однажды, почему это не интерфейс), HttpContext и т.д., И модульные тесты превращаются в насмешливый ад.
Я попытался использовать тестирование интеграции, чтобы справиться с этим, создав TestServer и получив всю инфраструктуру ASP.NET для каждого вызова api. Он работает довольно хорошо, но иногда кажется излишним, если я хочу проверить простую логику моего обработчика. И хотя он решает техническую проблему издевательской инфраструктуры ASP.NET, он сохраняет архитектурную проблему (если вы так считаете) о наличии инфраструктуры ASP.NET в своих обработчиках.
Я хотел бы знать, какие рекомендуемые подходы к его решению?
Ответы
Ответ 1
Я чувствую твою боль. Я наткнулся на фантастический пост от Джимми Богарда, который решает эту проблему, используя то, что Мартин Фаулер называет " Подкожные тесты". Я оставлю глубокое объяснение этим экспертам, но в двух словах подкожные тесты просто избегают всех трудных для тестирования аспектов пользовательского интерфейса.
Бесстыдный плагин: я сейчас нахожусь в процессе написания вики, демонстрирующей эти паттерны в образце сквозного проекта на github. За ним не трудно следовать, но, вероятно, слишком много кода для публикации SO ответа.
Подвести итоги:
- Если вы используете MediatR правильно, ваши контроллеры должны быть очень тонкими, что делает их тестирование бессмысленным.
- То, что вы хотите проверить, это ваши обработчики.
- Однако вы хотите проверить свои обработчики как часть вашего реального конвейера.
Решать:
- Оберните запрос http в транзакции.
- Создайте тестовое устройство, имитирующее приложения Startup.cs
- Настроить тестовый сервер БД для выполнения запросов и команд, а также сбрасывать после каждого теста.
Это в основном это. Каждый раз, когда вы запускаете интеграционный тест для одного из ваших обработчиков:
- Хостинговая среда является поддельной, но ваше приложение запускается в реальном тестировании.
- Ваш запрос или команда заключены в транзакцию, имитирующую ваш DbContext.
- Обработчик выполняется для реальной базы данных и затем сбрасывается.
Я бы добавил больше примеров кода в мой ответ, но между постом в блоге и предоставленной мною вики гораздо проще следовать там примерам кода.
Ответ 2
Я бы сказал, это зависит от уровня уверенности, который вы хотите получить в конце. Если вы хотите, чтобы вся система работала так, как ожидалось, возможно, интеграционные тесты с использованием TestServer
- это путь.
Одно из преимуществ MediatR заключается в том, что он позволяет отделить вашу бизнес-логику от приложения, использующего его, поэтому на самом верхнем уровне, допустим, в контроллерах нет логики, а просто делегирования посреднику.
Сказав это, вы правы, что иногда вашей логике нужна информация от хостинга. Примером может служить пользователь, делающий запрос, доступный в контексте HTTP.
В этом случае, если вы хотите избежать установки тестового HTTP-сервера для проверки работы вашей логики, вы можете представить эту информацию в абстракции, и тогда ваш обработчик будет зависеть от этой абстракции. Тогда ваши тесты могли бы издеваться над этой зависимостью, используя реальную систему для всего остального.
Это имеет смысл?
Ответ 3
Mediatr или нет, вы всегда должны стараться, чтобы только основные правила проходили по логике в ваших контроллерах и вызывали из них классы бизнес-логики, чтобы выполнять фактическую работу. Когда вы вводите им интерфейсы в эту бизнес-логику, зависимости ваших контроллеров легко высмеиваются в ваших модульных тестах, и ваши тесты могут сфокусироваться, если они правильно реализуют эти интерфейсы и выполняют только основную работу по вводу/выводу маршрутизации. И ваша фактическая бизнес-логика может быть протестирована еще проще.
Для тех статических классов, которые, например, для чтения настроек web.config, одна из стратегий, которые мне нравятся, создает вокруг них интерфейс оболочки. В то время как ConfigurationManager статичен, я все еще могу просто написать обычный класс с интерфейсом, в который я помещал методы или свойства для чтения определенного параметра (предпочтительно семантически названного) из Configuration Manager. Теперь я могу легко высмеять любой сконфигурированный параметр (или его отсутствие) в своем тесте, просто издеваясь над интерфейсом и настраивая разные возвращаемые значения.