Freigeben über


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 von IDistributedApplicationTestingBuilder, 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.
  • Bei appHost wurde die IDistributedApplicationTestingBuilder.BuildAsync(CancellationToken) Methode aufgerufen, die die DistributedApplication Instanz als appzurückgibt.
  • Für die HttpClient Ressource wird ein webfrontend erstellt, indem app.CreateHttpClientaufgerufen wird.
  • Die resourceNotificationService wird verwendet, um zu warten, bis die webfrontend 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 OKist.

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:

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.

Siehe auch