次の方法で共有


最初の .NET.NET Aspire テストを記述する

この記事では、テスト プロジェクトを作成し、テストを記述し、.NET.NET Aspire ソリューション用に実行する方法について説明します。 この記事のテストは単体テストではなく、機能テストまたは統合テストです。 .NET .NET Aspire には、.NET リソースの依存関係とその通信をテストするために使用できる、.NET Aspire のバリエーションがいくつか含まれています。 テスト プロジェクト テンプレートは、MSTest、NUnit、および xUnit テスト フレームワークで使用でき、テストの開始点として使用できるサンプル テストが含まれています。

.NET .NET Aspire テスト プロジェクト テンプレートは、📦Aspireに依存します。Hosting.Testing NuGet パッケージ。 このパッケージは、分散アプリケーションのテスト ホストを作成するために使用される DistributedApplicationTestingBuilder クラスを公開します。 分散アプリケーション テスト ビルダーは、DistributedApplication クラスに依存して、アプリ ホストを作成して起動します。

テスト プロジェクトを作成する

.NET .NET Aspire テスト プロジェクトを作成する最も簡単な方法は、テスト プロジェクト テンプレートを使用することです。 新しい .NET.NET Aspire プロジェクトを開始し、テスト プロジェクトを含める場合、Visual Studio ツールはそのオプションをサポートします。 既存の .NET.NET Aspire プロジェクトにテスト プロジェクトを追加する場合は、dotnet new コマンドを使用してテスト プロジェクトを作成できます。

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

詳細については、.NET CLI dotnet new コマンドのドキュメントを参照してください。

テスト プロジェクトを調べる

次のサンプル テスト プロジェクトは、.NET.NET Aspire Starter Application テンプレートの一部として作成されました。 使い慣れていない場合は、「クイック スタート: .NET.NET Aspire プロジェクトの最初のをビルドする」を参照してください。 .NET .NET Aspire テスト プロジェクトは、ターゲット アプリ ホストに対するプロジェクト参照の依存関係を受け取ります。 テンプレート プロジェクトについて考えてみましょう。

<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>

上記のプロジェクト ファイルはかなり標準的です。 PackageReference 📦のための Aspire があり、Hosting.Testing NuGet パッケージには、.NET.NET Aspire プロジェクトのテストを記述するために必要な型が含まれています。

テンプレート テスト プロジェクトには、1 つのテストを含む IntegrationTest1 クラスが含まれています。 このテストでは、次のシナリオが検証されます。

  • アプリ ホストが正常に作成され、開始されました。
  • webfrontend リソースが使用可能で実行中です。
  • webfrontend リソースに対して HTTP 要求を行い、正常な応答を返すことができます (HTTP 200 OK)。

次のテスト クラスについて考えてみましょう。

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

上記のコード:

  • DistributedApplicationTestingBuilder.CreateAsync API を使用して、アプリ ホストを非同期的に作成します。
    • appHost は、アプリ ホストを表す IDistributedApplicationTestingBuilder のインスタンスです。
    • appHost インスタンスには、標準の HTTP 回復性ハンドラーを使用してサービス コレクションが構成されています。 詳細については、「回復性の高い HTTP アプリの構築 : 主要な開発パターン」を参照してください。
  • appHost では、IDistributedApplicationTestingBuilder.BuildAsync(CancellationToken) メソッドが呼び出され、DistributedApplication インスタンスが appとして返されます。
    • appのサービスプロバイダがResourceNotificationServiceインスタンスを取得します。
    • app は非同期的に開始されます。
  • HttpClientを呼び出すことによって、webfrontend リソースの app.CreateHttpClient が作成されます。
  • resourceNotificationService は、webfrontend リソースが使用可能で実行されるのを待機するために使用されます。
  • 単純な HTTP GET 要求は、webfrontend リソースのルートに対して行われます。
  • テストでは、応答状態コードが OKされていることをアサートします。

リソース環境変数をテストする

.NET .NET Aspire ソリューションでリソースとその明示的な依存関係をさらにテストするには、環境変数が正しく挿入されていることをアサートできます。 次の例では、webfrontend リソースに、apiservice リソースに解決される HTTPS 環境変数があることをテストする方法を示します。

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

上記のコード:

概要

.NET Aspire テスト プロジェクト テンプレートを使用すると、.NET Aspire ソリューションのテスト プロジェクトを簡単に作成できます。 テンプレート プロジェクトには、テストの開始点として使用できるサンプル テストが含まれています。 DistributedApplicationTestingBuilder は、WebApplicationFactory<TEntryPoint>の ASP.NET Core に慣れたパターンに従います。 これにより、分散アプリケーションのテスト ホストを作成し、それに対してテストを実行できます。

最後に、DistributedApplicationTestingBuilder を使用すると、既定ですべてのリソース ログが DistributedApplication にリダイレクトされます。 リソース ログのリダイレクトにより、リソースが正しくログ記録されていることをアサートするシナリオが可能になります。

関連項目