Управляйте хостом приложения в тестах .NET.NET Aspire
При написании функциональных или интеграционных тестов с помощью .NET.NET Aspireэффективное управление экземпляром узла хоста приложения имеет решающее значение. Хост приложения представляет полную среду выполнения приложения и может быть дорогостоящим для создания и удаления. В этой статье объясняется, как управлять экземпляром хоста приложения в ваших тестах .NET.NET Aspire.
Для написания тестов с помощью .NET.NET Aspireиспользуется пакет NuGet 📦 Aspire.Hosting.Testing
, содержащий некоторые вспомогательные классы для управления экземпляром узла приложения в тестах.
Использование класса DistributedApplicationTestingBuilder
В руководстве по написанию вашего первого теставы познакомились с классом DistributedApplicationTestingBuilder, который можно использовать для создания экземпляра хоста приложения.
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
Метод DistributedApplicationTestingBuilder.CreateAsync<T>
принимает тип проекта узла приложения в качестве универсального параметра для создания экземпляра узла приложения. Хотя этот метод выполняется в начале каждого теста, он более эффективен для создания экземпляра узла приложения один раз и совместного использования в тестах по мере роста набора тестов.
С помощью xUnit вы реализуете интерфейс IAsyncLifetime в тестовом классе для поддержки асинхронной инициализации и удаления экземпляра узла приложения. Метод InitializeAsync
используется для создания экземпляра узла приложения перед выполнением тестов, а метод DisposeAsync
удаляет узел приложения после завершения тестов.
public class WebTests : IAsyncLifetime
{
private DistributedApplication _app;
public async Task InitializeAsync()
{
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
_app = await appHost.BuildAsync();
}
public async Task DisposeAsync() => await _app.DisposeAsync();
[Fact]
public async Task GetWebResourceRootReturnsOkStatusCode()
{
// test code here
}
}
В MSTest вы используете ClassInitializeAttribute и ClassCleanupAttribute для статических методов тестового класса, чтобы обеспечить инициализацию и очистку экземпляра узла приложения. Метод ClassInitialize
используется для создания экземпляра узла приложения перед выполнением тестов, а метод ClassCleanup
удаляет экземпляр узла приложения после завершения тестов.
[TestClass]
public class WebTests
{
private static DistributedApplication _app;
[ClassInitialize]
public static async Task ClassInitialize(TestContext context)
{
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
_app = await appHost.BuildAsync();
}
[ClassCleanup]
public static async Task ClassCleanup() => await _app.DisposeAsync();
[TestMethod]
public async Task GetWebResourceRootReturnsOkStatusCode()
{
// test code here
}
}
В NUnit вы используете атрибуты OneTimeSetUp и OneTimeTearDown для методов тестового класса, чтобы обеспечить настройку и демонтаж экземпляра хоста приложения. Метод OneTimeSetUp
используется для создания экземпляра узла приложения перед выполнением тестов, а метод OneTimeTearDown
удаляет экземпляр узла приложения после завершения тестов.
public class WebTests
{
private DistributedApplication _app;
[OneTimeSetUp]
public async Task OneTimeSetup()
{
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
_app = await appHost.BuildAsync();
}
[OneTimeTearDown]
public async Task OneTimeTearDown() => await _app.DisposeAsync();
[Test]
public async Task GetWebResourceRootReturnsOkStatusCode()
{
// test code here
}
}
Захватив хост приложения в поле во время начала тестового запуска, вы можете получить доступ к нему в каждом тесте без необходимости повторно его создавать, уменьшая время, необходимое для выполнения тестов. После завершения тестового запуска хост приложения удаляется, что освобождает все ресурсы, созданные во время тестового запуска, например контейнеры.
Передача аргументов в хост приложения
Вы можете получить доступ к аргументам из узла приложения с помощью параметра args
. Аргументы также передаются в системы конфигурации.NET, чтобы можно было переопределить множество параметров конфигурации таким образом. В следующем примере вы переопределите среду , указав ее в качестве параметра командной строки:
var builder = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.MyAppHost>(
[
"--environment=Testing"
]);
Другие аргументы можно передать хосту приложения Program
и сделать доступными в хосте приложения. В следующем примере вы передаете аргумент в узел приложения и используете его для управления добавлением томов данных в экземпляр Postgres.
В узле приложения Program
используется конфигурация для поддержки включения или отключения томов:
var postgres = builder.AddPostgres("postgres1");
if (builder.Configuration.GetValue("UseVolumes", true))
{
postgres.WithDataVolume();
}
В тестовом коде вы передаете "UseVolumes=false"
в args
к хосту приложения.
public async Task DisableVolumesFromTest()
{
// Disable volumes in the test builder via arguments:
using var builder = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.TestingAppHost1_AppHost>(
[
"UseVolumes=false"
]);
// The container will have no volume annotation since we disabled volumes by passing UseVolumes=false
var postgres = builder.Resources.Single(r => r.Name == "postgres1");
Assert.Empty(postgres.Annotations.OfType<ContainerMountAnnotation>());
}
Использование класса DistributedApplicationFactory
Хотя класс DistributedApplicationTestingBuilder
полезен для многих сценариев, могут возникнуть ситуации, когда требуется больше контроля над запуском узла приложения, например выполнение кода до создания построителя или после создания узла приложения. В этих случаях вы реализуете собственную версию класса DistributedApplicationFactory. Это то, что DistributedApplicationTestingBuilder
использует в своей внутренней системе.
public class TestingAspireAppHost()
: DistributedApplicationFactory(typeof(Projects.AspireApp_AppHost))
{
// override methods here
}
Конструктору требуется тип ссылки на проект узла приложения в качестве параметра. При необходимости можно указать аргументы в конструкторе хост-приложения. Эти аргументы управляют запуском узла приложения и предоставлением значений переменной args, используемой файлом Program.cs для запуска экземпляра узла приложения.
Методы жизненного цикла
Класс DistributionApplicationFactory
предоставляет несколько методов жизненного цикла, которые можно переопределить для обеспечения пользовательского поведения во время подготовки и создания узла приложения. Доступные методы: OnBuilderCreating
, OnBuilderCreated
, OnBuilding
и OnBuilt
.
Например, мы можем использовать метод OnBuilderCreating
для задания конфигурации, включающей сведения о подписке и группе ресурсов для Azure, до того как будет создан узел приложения и подготовлены все зависимые Azure ресурсы, чтобы наши тесты использовали правильную среду Azure.
public class TestingAspireAppHost() : DistributedApplicationFactory(typeof(Projects.AspireApp_AppHost))
{
protected override void OnBuilderCreating(DistributedApplicationOptions applicationOptions, HostApplicationBuilderSettings hostOptions)
{
hostOptions.Configuration ??= new();
hostOptions.Configuration["environment"] = "Development";
hostOptions.Configuration["AZURE_SUBSCRIPTION_ID"] = "00000000-0000-0000-0000-000000000000";
hostOptions.Configuration["AZURE_RESOURCE_GROUP"] = "my-resource-group";
}
}
Из-за порядка приоритета в системе конфигурации .NET переменные среды будут использоваться над любым элементом в файле appsettings.json или secrets.json.
Другой сценарий, который может потребоваться использовать в жизненном цикле, заключается в настройке служб, используемых узлом приложения. В следующем примере рассмотрим сценарий, в котором вы переопределите API OnBuilderCreated
, чтобы добавить устойчивость к HttpClient
:
protected override void OnBuilderCreated(
DistributedApplicationBuilder applicationBuilder)
{
applicationBuilder.Services.ConfigureHttpClientDefaults(clientBuilder =>
{
clientBuilder.AddStandardResilienceHandler();
});
}
См. также
.NET Aspire