Schreiben Sie Ihren ersten .NET.NET Aspire Test
In diesem Artikel erfahren Sie, wie Sie ein Testprojekt erstellen, Tests schreiben und für Ihre .NET.NET Aspire Lösungen ausführen. Die Tests in diesem Artikel sind keine Komponententests, sondern funktionale oder Integrationstests. .NET .NET Aspire umfasst mehrere Variationen von Testprojektvorlagen , mit denen Sie die Abhängigkeiten Ihrer .NET.NET Aspire-Ressourcen und deren Kommunikation testen können. Die Testprojektvorlagen sind für MSTest-, NUnit- und xUnit-Testframeworks verfügbar und enthalten einen Beispieltest, den Sie als Ausgangspunkt für Ihre Tests verwenden können.
Die .NET.NET Aspire Testprojektvorlagen basieren auf dem 📦Aspire. Hosting.Testing NuGet-Paket. Dieses Paket macht die DistributedApplicationTestingBuilder Klasse verfügbar, die zum Erstellen eines Testhosts für Ihre verteilte Anwendung verwendet wird. Der Generator für verteilte Anwendungstests basiert auf der DistributedApplication Klasse, um den App-Hostzu erstellen und zu starten.
Erstellen eines Testprojekts
Die einfachste Möglichkeit zum Erstellen eines .NET.NET Aspire Testprojekts besteht darin, die Testprojektvorlage zu verwenden. Wenn Sie ein neues .NET.NET Aspire Projekt starten und Testprojekte einbeziehen möchten, unterstützt das Visual Studio Tooling diese Option. Wenn Sie einem vorhandenen .NET.NET Aspire Projekt ein Testprojekt hinzufügen, können Sie den Befehl dotnet new
verwenden, um ein Testprojekt zu erstellen:
dotnet new aspire-xunit
dotnet new aspire-mstest
dotnet new aspire-nunit
Weitere Informationen finden Sie in der .NET CLI dotnet new Befehlsdokumentation.
Erkunden des Testprojekts
Das folgende Beispieltestprojekt wurde als Teil der Vorlage .NET.NET Aspire Starter Application erstellt. Wenn Sie damit nicht vertraut sind, siehe Schnellstart: Ihr erstes .NET.NET Aspire Projekterstellen. Das .NET.NET Aspire Testprojekts verwendet eine Projektreferenzabhängigkeit vom Ziel-App-Host. Betrachten Sie das Vorlagenprojekt:
<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.2" />
<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.6.4" />
</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.1" />
<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>
Die vorherige Projektdatei ist ziemlich standard. Es gibt ein PackageReference
-zu--📦-Aspire-Hosting.Testing--NuGet-Paket, das die erforderlichen Typen zum Schreiben von Tests für .NET-.NET Aspire-Projekte enthält.
Das Vorlagentestprojekt enthält eine IntegrationTest1
-Klasse mit einem einzelnen Test. Der Test überprüft das folgende Szenario:
- Der App-Host wurde erfolgreich erstellt und gestartet.
- Die
webfrontend
Ressource ist verfügbar und läuft. - An die
webfrontend
Ressource kann eine HTTP-Anforderung gesendet werden und eine erfolgreiche Antwort (HTTP 200 OK) zurückgegeben werden.
Berücksichtigen Sie die folgende Testklasse:
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));
}
}
Der vorangehende Code:
- Basiert auf der DistributedApplicationTestingBuilder.CreateAsync-API, um den App-Host asynchron zu erstellen.
- Die
appHost
ist eine Instanz vonIDistributedApplicationTestingBuilder
, die den App-Host darstellt. - Die
appHost
-Instanz ist mit einer Dienstsammlung konfiguriert, die den Standard-HTTP-Resilienz-Handler verwendet. Weitere Informationen finden Sie unter Erstellen robuster HTTP-Apps: Wichtige Entwicklungsmuster.
- Die
- Bei
appHost
wurde die IDistributedApplicationTestingBuilder.BuildAsync(CancellationToken) Methode aufgerufen, die dieDistributedApplication
Instanz alsapp
zurückgibt.- Der
app
veranlasst seinen Dienstanbieter, die Instanz ResourceNotificationService zu erhalten. - Die
app
wird asynchron gestartet.
- Der
- Für die HttpClient Ressource wird ein
webfrontend
erstellt, indemapp.CreateHttpClient
aufgerufen wird. - Die
resourceNotificationService
wird verwendet, um zu warten, bis diewebfrontend
Ressource verfügbar und ausgeführt wird. - Eine einfache HTTP GET-Anforderung wird an den Stamm der
webfrontend
Ressource gesendet. - Der Test bestätigt, dass der Antwortstatuscode
OK
ist.
Testen von Ressourcenumgebungsvariablen
Um Ressourcen und deren ausgedrückte Abhängigkeiten in Ihrer .NET.NET Aspire-Lösung weiter zu testen, können Sie sicherstellen, dass Umgebungsvariablen korrekt eingefügt werden. Im folgenden Beispiel wird veranschaulicht, wie Sie testen, dass die webfrontend
Ressource über eine HTTPS-Umgebungsvariable verfügt, die in die apiservice
Ressource aufgelöst wird:
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}")));
}
}
Der vorangehende Code:
- Basiert auf der DistributedApplicationTestingBuilder.CreateAsync-API, um den App-Host asynchron zu erstellen.
- Die
builder
-Instanz wird verwendet, um eine IResourceWithEnvironment-Instanz mit dem Namen "webfrontend" aus der IDistributedApplicationTestingBuilder.Resourcesabzurufen. - Die
webfrontend
Ressource wird verwendet, um GetEnvironmentVariableValuesAsync zum Abrufen der konfigurierten Umgebungsvariablen aufzurufen. - Das DistributedApplicationOperation.Publish-Argument wird beim Aufrufen von
GetEnvironmentVariableValuesAsync
übergeben, um Umgebungsvariablen anzugeben, die als Bindungsausdrücke für die Ressource veröffentlicht werden. - Mit den zurückgegebenen Umgebungsvariablen bestätigt der Test, dass die
webfrontend
Ressource eine HTTPS-Umgebungsvariable hat, die sich auf dieapiservice
Ressource auflöst.
Zusammenfassung
Die .NET Aspire Testprojektvorlage erleichtert das Erstellen von Testprojekten für .NET Aspire Lösungen. Das Vorlagenprojekt enthält einen Beispieltest, den Sie als Ausgangspunkt für Ihre Tests verwenden können. Die DistributedApplicationTestingBuilder
folgt einem vertrauten Muster wie die WebApplicationFactory<TEntryPoint> in ASP.NET Core. Es ermöglicht Ihnen, einen Testhost für Ihre verteilte Anwendung zu erstellen und Tests auszuführen.
Schließlich werden bei Verwendung der DistributedApplicationTestingBuilder
alle Ressourcenprotokolle standardmäßig an die DistributedApplication
umgeleitet. Die Umleitung von Ressourcenprotokollen ermöglicht Szenarien, in denen Sie bestätigen möchten, dass eine Ressource ordnungsgemäß protokolliert wird.