Escreva seu primeiro teste de .NET.NET Aspire
Neste artigo, você aprenderá a criar um projeto de teste, gravar testes e executá-los para suas soluções de .NET.NET Aspire. Os testes neste artigo não são testes de unidade, mas sim testes funcionais ou de integração. .NET .NET Aspire inclui várias variações de modelos de projeto de teste de que você pode usar para testar suas dependências de recursos .NET.NET Aspire e suas comunicações. Os modelos de projeto de teste estão disponíveis para estruturas de teste MSTest, NUnit e xUnit e incluem um teste de exemplo que você pode usar como ponto de partida para seus testes.
Os modelos de projeto de teste .NET.NET Aspire dependem do 📦Aspire.Hosting.Testing pacote NuGet. Esse pacote expõe a classe DistributedApplicationTestingBuilder, que é usada para criar um host de teste para seu aplicativo distribuído. O construtor de testes de aplicativo distribuído depende da classe DistributedApplication para criar e iniciar o host do aplicativo .
Criar um projeto de teste
A maneira mais fácil de criar um projeto de teste .NET.NET Aspire é usar o modelo de projeto de teste. Se você estiver iniciando um novo projeto de .NET.NET Aspire e quiser incluir projetos de teste, a ferramenta Visual Studio oferecerá suporte a essa opção. Se você estiver adicionando um projeto de teste a um projeto de .NET.NET Aspire existente, poderá usar o comando dotnet new
para criar um projeto de teste:
dotnet new aspire-xunit
dotnet new aspire-mstest
dotnet new aspire-nunit
Para obter mais informações, consulte a CLI .NETdocumentação do comando dotnet new.
Explorar o projeto de teste
O seguinte projeto de teste de exemplo foi criado como parte do modelo de Aplicativo Inicial .NET.NET Aspire. Se você não estiver familiarizado com isso, consulte Início Rápido: Criar seu primeiro projeto de .NET.NET Aspire. O projeto de teste .NET.NET Aspire usa uma dependência de referência de projeto no host do aplicativo de destino. Considere o projeto de modelo:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.Testing" Version="9.0.0" />
<PackageReference Include="coverlet.collector" Version="6.0.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AspireApp.AppHost\AspireApp.AppHost.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Net" />
<Using Include="Microsoft.Extensions.DependencyInjection" />
<Using Include="Aspire.Hosting.ApplicationModel" />
<Using Include="Aspire.Hosting.Testing" />
<Using Include="Xunit" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<PropertyGroup>
<EnableMSTestRunner>true</EnableMSTestRunner>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.Testing" Version="9.0.0" />
<PackageReference Include="MSTest" Version="3.7.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AspireApp.AppHost\AspireApp.AppHost.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Net" />
<Using Include="Microsoft.Extensions.DependencyInjection" />
<Using Include="Aspire.Hosting.ApplicationModel" />
<Using Include="Aspire.Hosting.Testing" />
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.Testing" Version="9.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="NUnit.Analyzers" Version="4.5.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AspireApp.AppHost\AspireApp.AppHost.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Net" />
<Using Include="Microsoft.Extensions.DependencyInjection" />
<Using Include="Aspire.Hosting.ApplicationModel" />
<Using Include="Aspire.Hosting.Testing" />
<Using Include="NUnit.Framework" />
</ItemGroup>
</Project>
O arquivo de projeto anterior é bastante padrão. Há uma PackageReference
para o 📦Aspire.Hosting.Testing pacote NuGet, que inclui os tipos necessários para escrever testes para projetos .NET.NET Aspire.
O projeto de teste de modelo inclui uma classe IntegrationTest1
com um único teste. O teste verifica o seguinte cenário:
- O host do aplicativo é criado e iniciado com êxito.
- O recurso
webfrontend
está disponível e em execução. - Uma solicitação HTTP pode ser feita ao recurso
webfrontend
e retorna uma resposta bem-sucedida (HTTP 200 OK).
Considere a seguinte classe de teste:
namespace AspireApp.Tests;
public class IntegrationTest1
{
[Fact]
public async Task GetWebResourceRootReturnsOkStatusCode()
{
// Arrange
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
appHost.Services.ConfigureHttpClientDefaults(clientBuilder =>
{
clientBuilder.AddStandardResilienceHandler();
});
// To output logs to the xUnit.net ITestOutputHelper,
// consider adding a package from https://www.nuget.org/packages?q=xunit+logging
await using var app = await appHost.BuildAsync();
var resourceNotificationService = app.Services
.GetRequiredService<ResourceNotificationService>();
await app.StartAsync();
// Act
var httpClient = app.CreateHttpClient("webfrontend");
await resourceNotificationService.WaitForResourceAsync(
"webfrontend",
KnownResourceStates.Running
)
.WaitAsync(TimeSpan.FromSeconds(30));
var response = await httpClient.GetAsync("/");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
namespace AspireApp.Tests;
[TestClass]
public class IntegrationTest1
{
[TestMethod]
public async Task GetWebResourceRootReturnsOkStatusCode()
{
// Arrange
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
appHost.Services.ConfigureHttpClientDefaults(clientBuilder =>
{
clientBuilder.AddStandardResilienceHandler();
});
await using var app = await appHost.BuildAsync();
var resourceNotificationService = app.Services
.GetRequiredService<ResourceNotificationService>();
await app.StartAsync();
// Act
var httpClient = app.CreateHttpClient("webfrontend");
await resourceNotificationService.WaitForResourceAsync(
"webfrontend",
KnownResourceStates.Running
)
.WaitAsync(TimeSpan.FromSeconds(30));
var response = await httpClient.GetAsync("/");
// Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
}
namespace AspireApp.Tests;
public class IntegrationTest1
{
[Test]
public async Task GetWebResourceRootReturnsOkStatusCode()
{
// Arrange
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
appHost.Services.ConfigureHttpClientDefaults(clientBuilder =>
{
clientBuilder.AddStandardResilienceHandler();
});
await using var app = await appHost.BuildAsync();
var resourceNotificationService = app.Services
.GetRequiredService<ResourceNotificationService>();
await app.StartAsync();
// Act
var httpClient = app.CreateHttpClient("webfrontend");
await resourceNotificationService.WaitForResourceAsync(
"webfrontend",
KnownResourceStates.Running
)
.WaitAsync(TimeSpan.FromSeconds(30));
var response = await httpClient.GetAsync("/");
// Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
}
}
O código anterior:
- Depende da API DistributedApplicationTestingBuilder.CreateAsync para criar de forma assíncrona o host do aplicativo.
- O
appHost
é uma instância deIDistributedApplicationTestingBuilder
que representa o host do aplicativo. - A instância
appHost
tem sua coleção de serviços configurada com o manipulador de resiliência HTTP padrão. Para obter mais informações, consulte Criar aplicativos HTTP resilientes: principais padrões de desenvolvimento.
- O
- O
appHost
tem seu método IDistributedApplicationTestingBuilder.BuildAsync(CancellationToken) invocado, que retorna a instânciaDistributedApplication
como oapp
.- O
app
utiliza seu provedor de serviços para obter a instância ResourceNotificationService. - O
app
é iniciado de forma assíncrona.
- O
- Um HttpClient é criado para o recurso
webfrontend
, chamando a funçãoapp.CreateHttpClient
. - O
resourceNotificationService
é usado para aguardar que o recursowebfrontend
esteja disponível e em execução. - Uma solicitação HTTP GET simples é feita na raiz do recurso
webfrontend
. - O teste afirma que o código de status de resposta é
OK
.
Testar variáveis de ambiente de recurso
Para testar ainda mais os recursos e suas dependências expressas em sua solução de .NET.NET Aspire, você pode afirmar que as variáveis de ambiente são injetadas corretamente. O exemplo a seguir demonstra como testar se o recurso webfrontend
tem uma variável de ambiente HTTPS que é resolvida para o recurso apiservice
:
using Aspire.Hosting;
namespace AspireApp.Tests;
public class EnvVarTests
{
[Fact]
public async Task WebResourceEnvVarsResolveToApiService()
{
// Arrange
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
var frontend = (IResourceWithEnvironment)appHost.Resources
.Single(static r => r.Name == "webfrontend");
// Act
var envVars = await frontend.GetEnvironmentVariableValuesAsync(
DistributedApplicationOperation.Publish);
// Assert
Assert.Contains(envVars, static (kvp) =>
{
var (key, value) = kvp;
return key is "services__apiservice__https__0"
&& value is "{apiservice.bindings.https.url}";
});
}
}
using Aspire.Hosting;
namespace AspireApp.Tests;
[TestClass]
public class EnvVarTests
{
[TestMethod]
public async Task WebResourceEnvVarsResolveToApiService()
{
// Arrange
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
var frontend = (IResourceWithEnvironment)appHost.Resources
.Single(static r => r.Name == "webfrontend");
// Act
var envVars = await frontend.GetEnvironmentVariableValuesAsync(
DistributedApplicationOperation.Publish);
// Assert
CollectionAssert.Contains(envVars,
new KeyValuePair<string, string>(
key: "services__apiservice__https__0",
value: "{apiservice.bindings.https.url}"));
}
}
using Aspire.Hosting;
namespace AspireApp.Tests;
public class EnvVarTests
{
[Test]
public async Task WebResourceEnvVarsResolveToApiService()
{
// Arrange
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
var frontend = (IResourceWithEnvironment)appHost.Resources
.Single(static r => r.Name == "webfrontend");
// Act
var envVars = await frontend.GetEnvironmentVariableValuesAsync(
DistributedApplicationOperation.Publish);
// Assert
Assert.That(envVars, Does.Contain(
new KeyValuePair<string, string>(
key: "services__apiservice__https__0",
value: "{apiservice.bindings.https.url}")));
}
}
O código anterior:
- Depende da API DistributedApplicationTestingBuilder.CreateAsync para criar de forma assíncrona o host do aplicativo.
- A instância de
builder
é usada para recuperar uma instância de IResourceWithEnvironment chamada "webfrontend" do IDistributedApplicationTestingBuilder.Resources. - O recurso
webfrontend
é usado para chamar GetEnvironmentVariableValuesAsync para recuperar suas variáveis de ambiente configuradas. - O argumento DistributedApplicationOperation.Publish é passado ao chamar
GetEnvironmentVariableValuesAsync
para especificar variáveis de ambiente que são publicadas no recurso como expressões de associação. - Com as variáveis de ambiente retornadas, o teste afirma que o recurso
webfrontend
tem uma variável de ambiente HTTPS que é resolvida para o recursoapiservice
.
Resumo
O modelo de projeto de teste de .NET Aspire facilita a criação de projetos de teste para soluções de .NET Aspire. O projeto de modelo inclui um teste de exemplo que você pode usar como ponto de partida para seus testes. O DistributedApplicationTestingBuilder
segue um padrão familiar à WebApplicationFactory<TEntryPoint> em ASP.NET Core. Ele permite que você crie um host de teste para seu aplicativo distribuído e execute testes nele.
Por fim, ao usar o DistributedApplicationTestingBuilder
todos os logs de recursos são redirecionados para o DistributedApplication
por padrão. O redirecionamento de logs de recursos permite cenários em que você deseja afirmar que um recurso está registrando corretamente.