Partager via


Rédigez votre premier test de .NET.NET Aspire

Dans cet article, vous allez apprendre à créer un projet de test, à écrire des tests et à les exécuter pour vos solutions .NET.NET Aspire. Les tests de cet article ne sont pas des tests unitaires, mais plutôt des tests fonctionnels ou d’intégration. .NET .NET Aspire comprend plusieurs variantes de modèles de projet de test que vous pouvez utiliser pour tester vos dépendances de ressources .NET.NET Aspire et leurs communications. Les modèles de projet de test sont disponibles pour les frameworks de test MSTest, NUnit et xUnit et incluent un exemple de test que vous pouvez utiliser comme point de départ pour vos tests.

Les modèles de projet de test .NET.NET Aspire s’appuient sur le package NuGet 📦Aspire. Hosting.Testing. Ce package expose la classe DistributedApplicationTestingBuilder, qui est utilisée pour créer un hôte de test pour votre application distribuée. Le générateur de tests d’applications distribués s’appuie sur la classe DistributedApplication pour créer et démarrer l’hôte d’application .

Créer un projet de test

Le moyen le plus simple de créer un projet de test .NET.NET Aspire consiste à utiliser le modèle de projet de test. Si vous démarrez un nouveau projet .NET.NET Aspire et que vous souhaitez inclure des projets de test, l’outil Visual Studio prend en charge cette option. Si vous ajoutez un projet de test à un projet de .NET.NET Aspire existant, vous pouvez utiliser la commande dotnet new pour créer un projet de test :

dotnet new aspire-xunit
dotnet new aspire-mstest
dotnet new aspire-nunit

Pour plus d’informations, consultez la documentation de la .NET CLI dotnet new command.

Explorer le projet de test

L’exemple de projet de test suivant a été créé dans le cadre du modèle d'application de démarrage .NET.NET Aspire. Si vous ne le connaissez pas, consultez Démarrage rapide : Construire votre premier projet .NET.NET Aspire. Le projet de test .NET.NET Aspire utilise une dépendance de référence de projet sur l’hôte d’application cible. Considérez le projet de modèle :

<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.0" />
    <PackageReference Include="NUnit.Analyzers" Version="4.4.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>

Le fichier projet précédent est assez standard. Il y a un PackageReference au 📦Aspire. Hosting.Testing package NuGet, qui inclut les types requis pour écrire des tests pour .NET.NET Aspire projets.

Le projet de test de modèle inclut une classe IntegrationTest1 avec un seul test. Le test vérifie le scénario suivant :

  • L’hôte de l’application a été créé avec succès et démarré.
  • La ressource webfrontend est disponible et en cours d’exécution.
  • Une requête HTTP peut être envoyée à la ressource webfrontend et retourne une réponse réussie (HTTP 200 OK).

Considérez la classe de test suivante :

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));
    }
}

Code précédent :

  • S’appuie sur l’API DistributedApplicationTestingBuilder.CreateAsync pour créer de façon asynchrone l’hôte d’application.
  • La méthode IDistributedApplicationTestingBuilder.BuildAsync(CancellationToken) de appHost est invoquée, qui retourne l’instance DistributedApplication en tant que app.
    • Le app fait en sorte que son fournisseur de services obtienne l’instance ResourceNotificationService.
    • La app est démarrée de façon asynchrone.
  • Un HttpClient est créé pour la ressource webfrontend en appelant app.CreateHttpClient.
  • Le resourceNotificationService est utilisé pour attendre que la ressource webfrontend soit disponible et en cours d’exécution.
  • Une requête HTTP GET simple est effectuée à la racine de la ressource webfrontend.
  • Le test affirme que le code d’état de la réponse est OK.

Tester les variables d’environnement de ressources

Pour tester davantage les ressources et leurs dépendances exprimées dans votre solution .NET.NET Aspire, vous pouvez affirmer que les variables d’environnement sont injectées correctement. L’exemple suivant montre comment tester que la ressource webfrontend a une variable d’environnement HTTPS qui se résout vers la ressource 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}")));
    }
}

Code précédent :

Résumé

Le modèle de projet de test .NET Aspire facilite la création de projets de test pour .NET Aspire solutions. Le projet de modèle inclut un exemple de test que vous pouvez utiliser comme point de départ pour vos tests. Le DistributedApplicationTestingBuilder suit un modèle familier à l'WebApplicationFactory<TEntryPoint> dans ASP.NET Core. Il vous permet de créer un hôte de test pour votre application distribuée et d’exécuter des tests sur celui-ci.

Enfin, lors de l’utilisation de la DistributedApplicationTestingBuilder tous les journaux de ressources sont redirigés vers le DistributedApplication par défaut. La redirection des journaux de ressources permet des scénarios dans lesquels vous voulez vous assurer qu'une ressource journalise correctement.

Voir aussi