首页 > 编程语言 >[.NET Blog] .NET Aspire 测试入门

[.NET Blog] .NET Aspire 测试入门

时间:2024-09-30 10:27:36浏览次数:1  
标签:app await Blog var 测试 Aspire NET 我们

https://devblogs.microsoft.com/dotnet/getting-started-with-testing-and-dotnet-aspire/

自动化测试是软件开发的重要一环。它可以帮助我们尽早确认软件中的缺陷和防止回归问题。在本文中,我们将探讨如何在 .NET Aspire 中开始测试,支持我们进行跨分布式应用的测试场景。

测试分布式应用

分布式应用存在本质上的复杂性。你需要确保各种组件,例如数据库、缓存等等可用并且工作在正确的状态。然后,你的应用可能存在众多的服务需要一起进行测试。.NET Aspire 是非常棒的工具来帮助我们定义应用程序的环境,将众多的服务和资源连接在一起,进而比较容易地启动我们的环境。

对于端到端的测试也是这样,或者对于集成测试,我们需要确保数据库在期望的测试状态,避免其他的测试影响我们的测试,并且确保应用运行在正确的配置之下,这也是 .NET Aspire 可以提供帮助的地方。

谢天谢地,我们拥有了 .NET Aspire 的 Aspire.Hosting.Testing NuGet Package,它可以在这些方面帮助我们,让我们看一下我们可以如何使用它来编写测试用例。

入门

在开始的时候,我们将创建一个新的 .NET Aspire Starter App 项目,这将会创建一个新的使用了 AppHost 的 .NET Application,以及 Service Defaults, 和一个 API backend 和 一个 Blazor Web 前端项目。

首先确保你已经安装了 .NET Aspire workload

dotnet workload update
dotnet workload install aspire

然后,使用 aspire-starter 模板来创建新的项目。

dotnet new aspire-starter --name AspireWithTesting

然后,我们需要添加测试项目了,我们可以从 3 个测试框架中选择你希望的那个:

  • 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 项目中定义的所有资源、服务和它们的关系,然后启动它们,如同在 AppHost 项目中执行 dotnet run 一样。但是在测试项目中,我们可以控制一些额外的方面。例如,我们可以注入 StandardResilienceHandlerHttpClient 中,这样测试中可以可以用来与 AppHost 中的服务交互。在 AppHost 配置之后,我们构建整个应用,准备开始执行测试。

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

因为 AppHost 将要启动多个不同的资源和服务,我们需要在基于它们执行我们的测试之前,确保它们可用。在这之后,如果 Web 应用没有正常启动,而我们试图访问它,我们就会得到一个错误。ResourceNotificationService 是用来帮助我们等待某个服务进入特定状态的服务。在我们的这个示例中,我们将等待 Webfrontend (这是配置在 AppHost 中的名称) 进入 Running 状态,我们使用 30s 的时间来等待。这个模式将需要对任何我们需要交互的服务使用,不管是直接还是间接。

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

注意:由于这里涉及的 record 类型的 WeatherForecast 是在 API 项目中私有定义的,我们我们需要在测试项目中重新定义它,以便反序列化收到的 JSON 数据。

一旦我们断言 API 端点返回了期望的 200 OK 响应,我们可以进而反序列化收到的 JSON 响应为 WeatherForecast 对象的集合,并基于这些数据进一步断言。在我们的示例中,我们的 API 返回的是随机生成的数据,所以我们仅仅断言返回的 record 的数量,不过,如果我们的数据是来自于数据库中,那么,断言就可以根据期望的数据进行。

总结

在本文中,我们介绍了如何开始在 .NET Aspire 中进行测试。它使得我们可以处理分布式应用的测试场景。我们也看到了如何针对前端应用和 API 服务开发测试用例,确保它们运行并如期望进行响应。

Reference:

标签:app,await,Blog,var,测试,Aspire,NET,我们
From: https://www.cnblogs.com/haogj/p/18441261

相关文章

  • 转载 fastapi 部署 原文链接:https://blog.csdn.net/FrenzyTechAI/article/details/132
    sudoadd-apt-repositoryppa:deadsnakes/ppasudoaptupdatesudoaptinstallpython3.12python3.12-venv-ysudoaptinstallsupervisorsudoaptinstallsupervisornginx-y启用并启动Supervisor:sudosystemctlenablesupervisorsudosystemctlstartsupervisor使用ena......
  • 实战 vSphere 7 vMotion 迁移说明 ( Vcenetr 内迁移、 跨vcenter 迁移)
    目录一、vMotion简介二、Vcenter内虚拟机迁移2.1无共享存储的环境中的vMotion具有以下要求和限制2.2网卡启用vMotion​编辑2.3右键单击虚拟机,然后选择迁移2.4选择更改计算资源和存储2.5选择虚拟机的目标资源2.6选择虚拟机磁盘格式2.7选择虚拟机存储策略2.......
  • 转载 https://blog.csdn.net/h1773655323/article/details/142098658#:~:text=%E7%94%
    在Ubuntu20.04上安装Python3.12:详细教程写bug如流水于2024-09-1014:41:34发布阅读量837收藏11点赞数4分类专栏:Python文章标签:ubuntupythonlinux版权Python专栏收录该内容53篇文章7订阅订阅专栏今天这篇文章将指导您如何在Ubuntu20.04上安装Python3.12......
  • 《迁移学习》—— 将 ResNet18 模型迁移到食物分类项目中
    文章目录一、迁移学习的简单介绍1.迁移学习是什么?2.迁移学习的步骤二、数据集介绍三、代码实现1.步骤2.所用到方法介绍的文章链接3.完整代码一、迁移学习的简单介绍1.迁移学习是什么?迁移学习是指利用已经训练好的模型,在新的任务上进行微调。迁移学习可以加快模......
  • Github_以太网开源项目verilog-ethernet代码阅读与移植(五)
    实验背景在(四)中介绍了Github开源项目verilog-ethernet的移植思路,以及对MII接口和数据链路层等功能的仿真,下面介绍数据的跨时钟域传输,以太网数据传输过程和网络层数据传输相关的移植。实验内容数据的跨时钟域传输处理,以太网数据传输过程和网络层数据传输模块介绍与仿真。......
  • .NET|--WPF|--笔记合集|--依赖项属性|--5.附加属性
    前言附加属性是一个ExtensibleApplicationMarkupLanguage(XAML)概念。附加属性允许为派生自DependencyObject的任何XAML元素设置额外的属性/值对,即使该元素未在其对象模型中定义这些额外的属性。额外的属性可进行全局访问。附加属性通常定义为没有常规属性包装......
  • Netflix截屏录屏播放的视频黑屏技术原理
    数字版权管理(DigitalRightsManagement,DRM)奈飞(Netflix)等流媒体平台对截屏或录屏时显示黑屏的现象,主要是通过数字版权管理(DigitalRightsManagement,DRM)技术实现的。技术原理:DRM保护:奈飞使用了名为Widevine或PlayReady等DRM技术,这些技术确保在播放受版权保护的内容时,限制......
  • 数据库离程序员有多远 - cnblogs救园行动感想
    这两周,我参与了博客园的“2024救园行动”,成了终身会员。说实话,当初报名的时候,我心里还挺兴奋的,想着这下能和不少老朋友在这个社区里再次相聚。毕竟,在数据库行业摸爬滚打了这么多年,自认为也认识不少圈内人士,更不用说程序员群体了。可结果却有点出乎意料。当我翻看那500位终身会员的......
  • [GAN][图片异常检测]Unsupervised Anomaly Detection withGenerative Adversarial Net
    论文背景与目标:    本文旨在将GAN运用到图片异常检测中,并取得了一定的效果,该模型不仅能够检测已知的异常,还能够发现未曾标注的新异常。提出了结合GAN的生成和判别功能的新型异常评分方法。在无监督的前提下实现了异常图像的分割。通过利用GAN的潜在空间,提出了新的......
  • java-netty客户端断线重启
    背景经常会遇到netty客户端,因为网络等多种原因而断线,需要自动重连核心就是对连接服务端成功后,对ChannelFuture进行监听,核心代码如下f=b.connect("127.0.0.1",10004).sync();//(5)f.addListener(newChannelFutureListener(){......