编写第一个 .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 初学者应用程序 模板的一部分创建的。 如果不熟悉它,请参阅 快速入门:生成第一个 .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>
上述项目文件相当标准。
模板测试项目包含具有单个测试的 IntegrationTest1
类。 该测试验证以下方案:
- 已成功创建并启动应用主机。
-
webfrontend
资源可用且正在运行。 - 可以向
webfrontend
资源发出 HTTP 请求,并返回成功的响应(HTTP 200 正常)。
请考虑以下测试类:
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
资源可用且正在运行。 - 对
webfrontend
资源的根目录发出简单的 HTTP GET 请求。 - 测试断言该响应状态代码为
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}")));
}
}
前面的代码:
- 依赖于 DistributedApplicationTestingBuilder.CreateAsync API 异步创建应用主机。
-
builder
实例用于从 IResourceWithEnvironment中检索名为“webfrontend”的 IDistributedApplicationTestingBuilder.Resources 实例。 -
webfrontend
资源用于调用 GetEnvironmentVariableValuesAsync 以检索其配置的环境变量。 - 调用 DistributedApplicationOperation.Publish 以将发布到资源的环境变量指定为绑定表达式时,将传递
GetEnvironmentVariableValuesAsync
参数。 - 使用返回的环境变量,测试断言
webfrontend
资源具有解析为apiservice
资源的 HTTPS 环境变量。
总结
通过 .NET Aspire 测试项目模板,可以更轻松地为 .NET Aspire 解决方案创建测试项目。 模板项目包括一个示例测试,可以用作你的测试的起点。
DistributedApplicationTestingBuilder
遵循 WebApplicationFactory<TEntryPoint>中 ASP.NET Core 的熟悉模式。 它允许你为分布式应用程序创建测试主机,并针对它运行测试。
最后,使用 DistributedApplicationTestingBuilder
默认情况下会将所有资源日志重定向到 DistributedApplication
。 资源日志的重定向可以用于验证资源日志记录是否正确的情形。
另请参阅
- 使用 dotnet test 和 xUnit 在
中 单元测试 C# - MSTest 概述
- 使用 NUnit 和
Core 单元测试 C#