技术速递|测试和 .NET Aspire 入门

作者:Aaron Powell - Principal Cloud Advocate
排版:Alan Wang

自动化测试是软件开发的一个重要组成部分,有助于确保在早期发现漏洞并防止回归问题。在这篇博文中,我们将探讨如何开始在 .NET Aspire 中进行测试,以便我们能够测试分布式应用程序中的各种场景。

测试分布式应用程序

分布式应用程序本质上很复杂,您需要确保数据库、缓存等组件可用且处于正确状态。然后您的应用程序可能有多个需要一起测试的服务。.NET Aspire 是一款出色的工具,可帮助我们定义应用程序的环境,将所有服务和资源连接在一起,从而轻松启动我们的环境。

当涉及应用程序的端到端或集成测试时,情况也是如此。我们需要确保数据库处于测试的预期状态,避免其他测试干扰我们的测试,并确保应用程序在正确的配置下运行。.NET Aspire 可以帮助我们实现这一点。

幸运的是,我们有 .NET Aspire Aspire.Hosting.Testing NuGet 包可以帮助我们实现这一点。让我们看看如何使用这个包来编写测试。

入门

首先,我们将创建一个新的 .NET Aspire Starter App 项目。这将创建一个新的 .NET Aspire 应用程序,其中包含 AppHost、Service Defaults、API 后端和 Blazor Web 前端。

确保您已安装 .NET Aspire 工作组件:

dotnet workload update
dotnet workload install aspire

然后使用 aspire-starter 模板创建一个新项目:

dotnet new aspire-starter --name AspireWithTesting

接下来,我们需要添加一个测试项目,我们可以从三个测试框架中选择一个:MSTest、xUnit 或 Nunit。在本例中,我们将使用 MSTest。您可以使用 aspire-mstest 模板创建:

dotnet new aspire-mstest --name AspireWithTesting.Tests
dotnet sln add AspireWithTesting.Tests

注意

您可以在使用 aspire-starter 模板创建时通过添加 --test-framework MSTest(或其他框架)标志来包含测试项目。

模板已经引用了 Aspire.Hosting.Testing NuGet 包以及所选的测试框架(在本例中为 MSTest),所以我们需要做的最后一件事是在我们的测试项目中添加对 AppHost 项目的引用:

dotnet add AspireWithTesting.Tests reference AspireWithTesting.AppHost

编写测试

我们会发现测试项目中已经有一个存根测试文件 IntegrationTest1.cs,它描述了上述步骤并提供了可以编写的测试示例,但为了更好地理解流程,我们将从头开始。创建一个名为 FrontEndTests.cs 的新文件并添加以下代码:

namespace AspireWithTesting.Tests;
[TestClass]
public class FrontEndTests
{
    [TestMethod]
    public async Task CanGetIndexPage()
    {
        var appHost =
            await DistributedApplicationTestingBuilder
                    .CreateAsync<Projects.AspireWithTesting_AppHost>();
        appHost.Services.ConfigureHttpClientDefaults(clientBuilder =>
        {
            clientBuilder.AddStandardResilienceHandler();
        }
);
        await using var app = await appHost.BuildAsync();
        await app.StartAsync();
        var resourceNotificationService =
            app.Services.GetRequiredService<ResourceNotificationService>();
        await resourceNotificationService
            .WaitForResourceAsync("webfrontend", KnownResourceStates.Running)
            .WaitAsync(TimeSpan.FromSeconds(30));
        var httpClient = app.CreateHttpClient("webfrontend");
        var response = await httpClient.GetAsync("/");
        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
    }
}

太棒了,测试写好了,让我们运行它:

dotnet test

如果一切按计划进行,我们应该看到如下输出:

Test summary: total: 1, failed: 0, succeeded: 1, skipped: 0, duration: 0.9s

了解测试

让我们分析一下测试中发生的情况:

var appHost =
    await DistributedApplicationTestingBuilder
            .CreateAsync<Projects.AspireWithTesting_AppHost>();
appHost.Services.ConfigureHttpClientDefaults(clientBuilder =>
{
    clientBuilder.AddStandardResilienceHandler();
});
await using var app = await appHost.BuildAsync();
await app.StartAsync();

测试的第一部分使用 AppHost 项目,该项目定义了我们所有的资源、服务及其关系,然后像 dotnet run 项目一样启动它,但在测试环境中,我们可以控制一些其他方面。例如,我们将 StandardResilienceHandler 注入到 HttpClient 中,这个客户端将用于测试与 AppHost 中服务的交互。配置测试 AppHost 后,我们可以构建应用程序并准备启动。

var resourceNotificationService =
    app.Services.GetRequiredService<ResourceNotificationService>();
await resourceNotificationService
    .WaitForResourceAsync("webfrontend", KnownResourceStates.Running)
    .WaitAsync(TimeSpan.FromSeconds(30));

由于 AppHost 将启动几种不同的资源和服务,因此我们需要确保它们在我们进行测试之前已经可用。毕竟,如果 Web 应用程序尚未启动,而我们尝试解决它,我们将收到错误。ResourceNotificationService 是一种允许我们等待资源处于特定状态的服务,在本例中,我们正在等待 Web 前端(我们在 AppHost 中设置的名称)处于正在运行状态,我们给它 30 秒的时间。对于我们将要直接或间接交互的任何其他服务,都需要重复此模式。

var httpClient = app.CreateHttpClient("webfrontend");
var response = await httpClient.GetAsync("/");
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);

最后,我们可以从已启动的应用程序中请求 HttpClient 实例,并提供我们想要与之交互的服务的名称。这使用与应用程序其余部分相同的服务发现机制,因此我们不必担心服务正在运行的 URL 或端口。然后,我们可以向服务(在本例中为 Web 前端的根路径)发出请求,并检查是否收到 200 OK 响应,确认服务按预期运行并正常响应。

测试 API

测试 API 服务的方法与测试前端服务的方法非常相似,但由于它返回数据,我们可以更进一步,对返回的数据进行断言验证:

using System.Net.Http.Json;
namespace AspireWithTesting.Tests;
[TestClass]
public class ApiTests
{
    [TestMethod]
    public async Task CanGetWeatherForecast()
    {
        var appHost =
            await DistributedApplicationTestingBuilder.CreateAsync<Projects.AspireWithTesting_AppHost>();
        appHost.Services.ConfigureHttpClientDefaults(clientBuilder =>
        {
            clientBuilder.AddStandardResilienceHandler();
        }
);
        await using var app = await appHost.BuildAsync();
        await app.StartAsync();
        var resourceNotificationService =
            app.Services.GetRequiredService<ResourceNotificationService>();
        await resourceNotificationService
                .WaitForResourceAsync("apiservice", KnownResourceStates.Running)
                .WaitAsync(TimeSpan.FromSeconds(30));
        var httpClient = app.CreateHttpClient("apiservice");
        var response = await httpClient.GetAsync("/weatherforecast");
        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
        var forecasts = await response.Content.ReadFromJsonAsync<IEnumerable<WeatherForecast>>();
        Assert.IsNotNull(forecasts);
        Assert.AreEqual(5, forecasts.Count());
    }
    record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary);
}

注意

由于 WeatherForecast 记录在 API 项目中是私有的,因此我们需要在测试项目中定义它,以便能够反序列化 JSON 响应。

一旦我们断言 API 端点返回了 200 OK 响应,我们就可以将 JSON 响应反序列化为 WeatherForecast 对象集合,并根据数据进行断言。在本例中,我们 API 的数据是随机生成的,因此我们只对返回的记录数进行断言,但如果我们有一个数据库提供数据,那么测试就可以针对预期数据进行断言。

测试 API

总结

在这篇博文中,我们探讨了如何开始在 .NET Aspire 中进行测试,从而使我们能够测试分布式应用程序中各种场景。我们已经了解了如何为前端和 API 服务编写测试,以确保它们按预期运行和响应。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值