Тестирование служб и веб-приложений ASP.NET Core
Совет
Это содержимое является фрагментом из электронной книги, архитектуры микрослужб .NET для контейнерных приложений .NET, доступных в документации .NET или в виде бесплатного скачиваемого PDF-файла, который можно читать в автономном режиме.
Контроллеры — это центральный элемент любой службы ASP.NET Core API или веб-приложения ASP.NET MVC. Поэтому необходимо убедиться в том, что они работают так, как это требуется вашему приложению. Это можно сделать с помощью автоматических тестов, которые помогут также обнаружить имеющиеся ошибки до ввода контроллеров в рабочую среду.
Необходимо протестировать работу контроллера при допустимых и недопустимых значениях входных данных, а также проверить реакцию контроллера в сопоставлении с результатами бизнес-операции, которую он выполняет. Вместе с тем вам необходимо выполнить следующие тесты микрослужб.
Модульные тесты. Эти тесты позволяют проверить правильность работы отдельных компонентов приложения. С помощью утверждений тестируется API компонента.
Интеграционные тесты. Такие тесты позволяют проверить правильность взаимодействия компонента с внешними устройствами, например с базами данных. С помощью утверждений можно тестировать API компонента, пользовательский интерфейс и побочные эффекты от таких операций, как операции ввода-вывода в базах данных, ведение журнала и т. д.
Функциональные тесты для каждой микрослужбы. Позволяют проверить правильность работы приложения с точки зрения пользователя.
Тесты служб. Позволяют проверить, были ли протестированы варианты сквозного использования служб, включая одновременное тестирование нескольких служб. Для такого тестирования необходимо сначала подготовить среду. В данном случае это означает запуск служб (например, с помощью docker-compose up).
Реализация модульных тестов для веб-API ASP.NET Core
Модульное тестирование — это тестирование части приложения изолированно от его инфраструктуры и зависимостей. При модульном тестировании логики контроллера проверяется только содержимое отдельного действия или метода, а работа его зависимостей и самой платформы в целом не проверяется. Модульные тесты не выявляют проблемы с взаимодействием между компонентами. Для этого предназначены интеграционные тесты.
Выполняя модульное тестирование действий контроллера, следует сосредоточиться только на этих действиях. Модульное тестирование контроллера не учитывает такие аспекты, как фильтры, маршрутизация и привязка модели (сопоставление данных запроса с ViewModel или DTO). Благодаря их узкой направленности модульные тесты, как правило, проще создавать, а выполняются они быстрее. Правильно составленный набор модульных тестов можно выполнять часто без значительных затрат.
Модульные тесты реализуются на основе платформ тестирования, таких как xUnit.net MSTest, Moq и NUnit. Для примера приложения eShopOnContainers мы используем xUnit.
При написании модульного теста для контроллера веб-API создается экземпляр класса контроллера непосредственно с помощью нового ключевого слова в C#, чтобы тест выполнялся как можно быстрее. В следующем примере показано, как это можно сделать в случае использования платформы тестирования xUnit.
[Fact]
public async Task Get_order_detail_success()
{
//Arrange
var fakeOrderId = "12";
var fakeOrder = GetFakeOrder();
//...
//Act
var orderController = new OrderController(
_orderServiceMock.Object,
_basketServiceMock.Object,
_identityParserMock.Object);
orderController.ControllerContext.HttpContext = _contextMock.Object;
var actionResult = await orderController.Detail(fakeOrderId);
//Assert
var viewResult = Assert.IsType<ViewResult>(actionResult);
Assert.IsAssignableFrom<Order>(viewResult.ViewData.Model);
}
Реализация интеграционных и функциональных тестов для каждой микрослужбы
Как уже отмечалось, интеграционные и функциональные тесты имеют разное назначение. Однако способы реализации тех и других при тестировании контроллеров ASP.NET Core похожи. Поэтому в этом разделе мы сосредоточимся на интеграционных тестах.
При интеграционном тестировании проверяется, будут ли компоненты приложения правильно работать после сборки. Платформа ASP.NET Core поддерживает интеграционное тестирование с использованием платформ модульного тестирования и встроенного веб-сервера тестирования, который может использоваться для обработки запросов без нагрузки на сеть.
В отличие от модульного тестирования, при интеграционном тестировании часто затрагиваются различные аспекты инфраструктуры приложения, например базы данных, файловые системы, сетевые ресурсы, веб-запросы и ответы. При модульном тестировании используются имитации или макеты объектов вместо реальных объектов. Интеграционное тестирование предназначено для проверки того, что система работает должным образом с реальными объектами, поэтому при интеграционном тестировании не используются имитации. В процесс тестирования вовлекается инфраструктура — проверяется, например, выполнение запросов на доступ к базам данных или вызов одной службы другой службой.
Так как при интеграционном тестировании отрабатывается значительно больший, чем при модульном тестировании, объем кода, а также из-за того, что при этом затрагивается инфраструктура элементов, такие тесты обычно выполняются на порядки медленнее, чем модульные тесты. Поэтому рекомендуется ограничить количество интеграционных тестов.
Платформа ASP.NET Core содержит встроенный веб-сервер тестирования, который можно использовать для обработки HTTP-запросов без нагрузки на сеть. Это позволяет выполнять тесты быстрее, чем при работе с реальными веб-серверами. Веб-сервер тестирования (TestServer) доступен в компоненте NuGet как Microsoft.AspNetCore.TestHost. Его можно добавлять в проекты интеграционного тестирования и использовать для размещения приложений ASP.NET Core.
Как можно видеть в следующем коде, при создании интеграционных тестов для контроллеров ASP.NET Core создается экземпляр контроллера на сервере тестирования. Это похоже на HTTP-запрос, но выполняется быстрее.
public class PrimeWebDefaultRequestShould
{
private readonly TestServer _server;
private readonly HttpClient _client;
public PrimeWebDefaultRequestShould()
{
// Arrange
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>());
_client = _server.CreateClient();
}
[Fact]
public async Task ReturnHelloWorld()
{
// Act
var response = await _client.GetAsync("/");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal("Hello World!", responseString);
}
}
Дополнительные ресурсы
Стив Смит (Steve Smith). Тестирование контроллеров (ASP.NET Core)
https://learn.microsoft.com/aspnet/core/mvc/controllers/testingСтив Смит (Steve Smith). Тестирование интеграции (ASP.NET Core)
https://learn.microsoft.com/aspnet/core/test/integration-testsМодульное тестирование в .NET с помощью команды dotnet test
https://learn.microsoft.com/dotnet/core/testing/unit-testing-with-dotnet-testxUnit.net. Официальный сайт
https://xunit.net/Основные сведения о модульных тестах.
https://learn.microsoft.com/visualstudio/test/unit-test-basicsMoq. Репозиторий GitHub.
https://github.com/moq/moqNUnit. Официальный сайт
https://nunit.org/
Реализация тестов служб в приложении с несколькими контейнерами
Как уже упоминалось, при тестировании приложения с несколькими контейнерами необходимо, чтобы все микрослужбы были запущены на узле Docker или в кластере контейнера. Для выполнения сквозного тестирования служб, включающего в себя множество операций, затрагивающих несколько микрослужб, требуется развертывание и запуск всего приложения на узле Docker с помощью docker-compose up (или аналогичного механизма, если используется оркестратор). После запуска приложения и всех его служб вы можете выполнять сквозное интеграционное и функциональное тестирование.
Здесь можно использовать несколько методов. В файле docker-compose.yml, который используется для развертывания приложения, на уровне решения можно развернуть точку входа, чтобы можно было использовать тест dotnet. Можно также использовать другой файл Compose, который будет запускать ваши тесты в целевом образе. Используя другой файл Compose для интеграционного тестирования, включающего в себя микрослужбы и базы данных в контейнерах, можно гарантировать, что соответствующие данные будут всегда переведены в исходное состояние перед выполнением тестов.
После того как приложение Compose будет установлено и запущено, вы сможете воспользоваться преимуществами точек останова и исключений, если используется Visual Studio. Интеграционные тесты можно выполнять автоматически в конвейере непрерывной интеграции Azure DevOps Services или в любой другой системе CI/CD, которая поддерживает контейнеры Docker.
Тестирование в eShopOnContainers
Тесты для примера приложения (eShopOnContainers) были недавно реструктуризованы, и теперь существует четыре категории:
модульные тесты, обычные старые модульные тесты, содержащиеся в проектах {MicroserviceName}.UnitTests;
функциональные и интеграционные тесты микрослужб с тестовыми случаями, включающими инфраструктуру для каждой микрослужбы, но изолированные от других и содержащиеся в проектах {MicroserviceName}.FunctionalTests;
функциональные и интеграционные тесты приложений, ориентированные на интеграцию микрослужб, с тестовыми случаями, которые воздействуют на несколько микрослужб (содержатся в проекте Application.FunctionalTests);
Модульные и интеграционные тесты помещены в папку тестов в проекте микрослужб. Но управление тестами приложений и нагрузочными тестами отдельно осуществляется в корневой папке, как показано на рисунке 6-25.
Рис. 6-25. Структура папок тестов в eShopOnContainers
Функциональные и интеграционные тесты микрослужбы и приложения выполняются из Visual Studio с помощью обычного средства запуска тестов. При этом сначала нужно запустить необходимые службы инфраструктуры с помощью набора файлов docker-compose, содержащихся в папке test решения:
docker-compose-test.yml
version: '3.4'
services:
redis.data:
image: redis:alpine
rabbitmq:
image: rabbitmq:3-management-alpine
sqldata:
image: mcr.microsoft.com/mssql/server:2017-latest
nosqldata:
image: mongo
docker-compose-test.override.yml
version: '3.4'
services:
redis.data:
ports:
- "6379:6379"
rabbitmq:
ports:
- "15672:15672"
- "5672:5672"
sqldata:
environment:
- SA_PASSWORD=[PLACEHOLDER]
- ACCEPT_EULA=Y
ports:
- "5433:1433"
nosqldata:
ports:
- "27017:27017"
Внимание
Корпорация Майкрософт рекомендует использовать самый безопасный поток проверки подлинности. Если вы подключаетесь к SQL Azure, управляемые удостоверения для ресурсов Azure — это рекомендуемый метод проверки подлинности.
Таким образом, для запуска функциональных и интеграционных тестов необходимо сначала выполнить следующую команду из папки test решения:
docker-compose -f docker-compose-test.yml -f docker-compose-test.override.yml up
Как видите, эти файлы docker-compose всего лишь запускают микрослужбы Redis, RabbitMQ, SQL Server и MongoDB.
Дополнительные ресурсы
Модульное тестирование и интеграция в eShopOnContainers
https://github.com/dotnet-architecture/eShopOnContainers/wiki/Unit-and-integration-testingНагрузочное тестирование на eShopOnContainers
https://github.com/dotnet-architecture/eShopOnContainers/wiki/Load-testing