.NET Aspire Apps 集成测试
原文:https://fiodar.substack.com/p/integration-testing-dotnet-aspire-apps
对于软件开发来说,拥有自动化的覆盖测试非常重要。尽管手工测试在有些场合存在其价值,自动化测试对于支持我们验证应用的处理逻辑比任何手工测试更加高效。
自动化测试还支持我们检查在某一个地方的变更不会意外地破坏其它的任何部分,这是另一个手工测试复杂应用几乎不可能做到的领域,尽管我们对任何新的功能实现添加了测试 (甚至使用了 TDD 模式,在实现功能之前就增加了测试用例),但我们仍然会拥有之前添加的所有先前功能的所有先前添加的测试。
集成测试是一类特殊的自动化测试,它允许我们验证应用程序或模块是否按预期与其他组件一起工作。这些类型的测试可能使用真实的网络、真实的 HTTP 请求、真实的数据库连接等。
传统上,集成测试相当难以应用于编排的分布式应用程序。这是因为此类应用程序有许多必须相互协调的移动组件。这些组件的计算成本通常很高;因此,集成测试通常是为了模拟有限数量的服务间交互。
但是,.NET Aspire 非常巧妙地解决了这个问题。Microsoft 的设计人员提前考虑了集成测试。因此,针对它运行集成测试几乎与运行低级组件/单元测试一样简单。
此外,Aspire 甚至附带了一个集成测试项目的模板,因此我们不必在每次需要将测试添加到新的分布式应用程序时都搜索 Internet。这就是我们今天要讨论的内容。
在 Aspire 中设置集成测试
最为简单的设置一个测试项目的途径是,在通过 Visual Studio 中创建 Aspire Starter Project 应用的时候,直接选择上 Create test project 选项
如果您是 dotnet CLI 用户,则可以执行以下命令来创建具有所有依赖项的新 Aspire 测试项目:
dotnet new aspire-xunit
默认情况下,Aspire 测试项目模板使用 xUnit。但是,我们将讨论的所有概念也可以在 NUnit 和 MSTest 中实现。Aspire 测试库与框架无关,可与其中任何一个一起使用。
我们甚至不必使用默认测试模板。我们可以使用任何测试框架在 .NET 中创建标准测试项目,然后安装特定于 Aspire 集成测试的依赖项。
安装必须的依赖
在 此 GitHub 存储库中,我们有一个从 Aspire 入门项目模板创建的 Aspire 项目示例。它添加了一个名为 AspireApp.Tests 的测试项目。如果我们打开 AspireApp.Tests.csproj 文件,我们将发现其中引用了 Aspire.Hosting.Testing NuGet 包。这是包含 Aspire 集成测试所需的所有组件的软件包。
项目引用的其余包与测试框架相关,并不特定于 Aspire。如果我们使用不同的测试框架,我们会看到引用不同的包。
因此,我们拥有开始编写测试所需的所有依赖项。当我们创建新的 Aspire 入门项目时,最好从默认提供的测试方法开始。它很简单,很容易让我们了解,但它也演示了 Aspire 测试库的核心功能。
研究 Aspire 的测试方法
如果我们开发 AspireApp.Test 项目中的 WebTests.cs 代码文件,我们将会看到如下的测试方法:
[Fact]
public async Task GetWebResourceRootReturnsOkStatusCode()
{
// Arrange
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
await using var app = await appHost.BuildAsync();
await app.StartAsync();
// Act
var httpClient = app.CreateHttpClient("webfrontend");
var response = await httpClient.GetAsync("/");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
这是 arrange-act-assert 测试模式的一个相当标准的示例。此模式规定每个测试方法都包含以下不同的部分:
- Arrange 安排:我们设置了运行测试所需的所有服务。
- Act 行动:我们执行我们正在测试的操作。
- Assert 断言:我们将验证此操作的结果。
在此示例中(通常在 C# 中应用),我们用注释标记每个部分,以使测试方法尽可能易于理解。
让我们逐行介绍此方法以了解它在做什么。我们采取的第一个操作如下:
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
这段代码使用 Aspire.Hosting.Testing
库中的 DistributedApplicationTestingBuilder
类型。我们调用静态 CreateAsync<T>()
方法来初始化分布式和编排的 Aspire 应用程序。
我们在调用此方法时指定的类型是 Projects.AspireApp_AppHost
,它是我们 Aspire Host 项目的自动生成的表示形式。从本质上讲,我们正在创建整个 Aspire 应用程序的测试版本。
我们不需要太深入研究 test host 和 normal host 之间的区别,但了解测试 host 要轻量级得多是有帮助的。但不要被它很轻的事实所欺骗。我们仍然能够测试 Aspire 应用程序的所有核心功能,以及其编排的服务如何相互操作。测试版本的 Host 仍然使用真实网络和其他真实依赖项。
随后的 2 行代码,构建 Host 并启动它。
await using var app = await appHost.BuildAsync();
await app.StartAsync();
请注意,我们将 app 对象(表示整个 Aspire 应用程序)包装在 using 范围内。这是因为它是一个一次性对象,依赖于各种非托管资源,例如网络连接。一旦代码退出 using 范围,它就会完全释放这些资源。
随后的代码是这样的:
var httpClient = app.CreateHttpClient("webfrontend");
这段代码从 Aspire 测试主机 Host 中来创建 HttpClient 类的实例。因为我们从 Aspire 主机 Host 中创建它,所以我们使用 Aspire 服务发现来设置其基本 URL。在此示例中,我们在 webfrontend 的名称下注册一个 Blazor 应用。我们的 HTTP 客户端将使用该应用程序的基本 URL 进行配置。
接下来,我们向应用程序的基址发送一个 GET HTTP 请求,该地址用正斜杠表示:
var response = await httpClient.GetAsync("/");
最后,我们验证我们收到的是 HTTP OK (200) 的响应码。
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
这是我们对使用 xUnit 实现的基本示例测试方法的概述。现在让我们看看如何使用替代测试框架来实现相同的方法。
使用其它的测试框架
如果我们希望使用 NUnit 来替换掉 xUnit 测试框架,我们的测试方法将会变成这样。
[Test]
public async Task GetWebResourceRootReturnsOkStatusCode()
{
// Arrange
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
await using var app = await appHost.BuildAsync();
await app.StartAsync();
// Act
var httpClient = app.CreateHttpClient("webfrontend");
var response = await httpClient.GetAsync("/");
// Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
在这种情况下,xUnit 和 NUnit 之间的主要区别在于使用 [Test] 而不是 [Fact],使用 Assert.AreEqual() 而不是 Assert.Equal()。否则,测试的结构和逻辑将保持不变。
如果我们首选的测试框架是 MSTest,则方法将如下所示:
[TestMethod]
public async Task GetWebResourceRootReturnsOkStatusCode()
{
// Arrange
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
await using var app = await appHost.BuildAsync();
await app.StartAsync();
// Act
var httpClient = app.CreateHttpClient("webfrontend");
var response = await httpClient.GetAsync("/");
// Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
在 MSTest 中,属性 [TestMethod] 用于指示测试方法。断言是使用 Assert.AreEqual() 完成的,类似于 NUnit。测试的其余部分与原始测试的结构和逻辑保持一致。
扩展测试功能
在前面我们研究的测试方法中,您可能已经注意到的一点是它非常简单。事实上,它是如此简单,以至于它的用处可能非常有限。我们所做的只是向应用程序的主页发送一个非常基本的 HTTP 请求,并验证我们是否收到了 200 响应代码。
改进测试的一种方法是查看响应中返回的内容。这适用于具有用户界面的浏览器内应用程序和无头 API 应用程序。在 .NET 中有很多方法可以做到这一点,每种方法都有其优点和缺点。
我们将使用 AngleSharp NuGet 包,它允许我们解析网页上的 HTML 并轻松搜索其内容。从测试项目中引用此 NuGet 包后,我们可以创建以下帮助程序类,其中包含允许我们读取 HTTP 响应对象并分析其内容的方法:
AngleSharp 是一个 .NET 库,使您能够解析基于尖括号的超文本,如 HTML、SVG 和 MathML。该库还支持未经验证的 XML。AngleSharp 的一个重要方面是 CSS 也可以被解析。包含的解析器基于官方 W3C 规范构建。这将生成给定源代码的完美可移植 HTML5 DOM 表示,并确保与常青浏览器中的结果兼容。此外,标准的 DOM 功能(如 querySelector 或 querySelectorAll)也适用于树遍历。
using AngleSharp;
using AngleSharp.Html.Dom;
using AngleSharp.Io;
using System.Net.Http.Headers;
namespace AspireApp.Tests.Helpers;
public class HtmlHelpers
{
public static async Task<IHtmlDocument> GetDocumentAsync(HttpResponseMessage response)
{
var content = await response.Content.ReadAsStringAsync();
var document = await BrowsingContext.New()
.OpenAsync(ResponseFactory, CancellationToken.None);
return (IHtmlDocument)document;
void ResponseFactory(VirtualResponse htmlResponse)
{
htmlResponse
.Address(response.RequestMessage.RequestUri)
.Status(response.StatusCode);
MapHeaders(response.Headers);
MapHeaders(response.Content.Headers);
htmlResponse.Content(content);
void MapHeaders(HttpHeaders headers)
{
foreach (var header in headers)
{
foreach (var value in header.Value)
{
htmlResponse.Header(header.Key, value);
}
}
}
}
}
}
该方法将一个 HttpResponseMessage 转化为一个 AngleSharp 的 IHtmlDocument 对象,以方便后继的处理。
在我们这个分布式示例应用中,Aspire 应用的前端是一个 Blazor 应用,通过访问独立的 REST API 应用来获得数据,填充到它的气象页面中。
我们开发一个测试用来来检查气象页面是否拥有我们所期望的内容
[Fact]
public async Task GetWeatherReturnsRightContent()
{
// Arrange
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.AspireApp_AppHost>();
await using var app = await appHost.BuildAsync();
await app.StartAsync();
// Act
var httpClient = app.CreateHttpClient("webfrontend");
var response = await httpClient.GetAsync("/weather");
var responseBody = await HtmlHelpers.GetDocumentAsync(response);
var descriptionElement = responseBody.QuerySelector("p");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(descriptionElement);
Assert.Equal(
"This component demonstrates showing data loaded from a backend API service.",
descriptionElement.InnerHtml);
}
在这个测试方法中,我们不是简单地检查页面是否存在并返回适当的响应代码。我们还通过将页面内容传递给我们之前创建的 helper 方法来读取页面的内容。然后我们搜索第一个段落元素 p
的 HTML 元素,并检查其内容是否为 This component demonstrates showing data loaded from a backend API service.
现在,我们的测试更加有用。
组件测试呢?
虽然集成测试是有益的,而且我们在分布式应用程序中可能需要它们,但我们也可以从测试单个组件中受益。这些类型的测试要快得多,因为它们不依赖于任何真正的网络。他们直接测试代码。
针对 Blazor 应用程序编写组件测试的最佳方法之一是使用 Bunit NuGet 包。这个包甚至得到了 Microsoft 的认可,并在其页面上被提及,尽管它是由开源社区制作的。
我们需要在应用程序中安装两个 NuGet 包:Bunit 和 Bunit.TestDoubles。现在,让我们看看如何使用这个包来测试这个页面:
bUnit is a testing library for Blazor Components. Its goal is to make it easy to write comprehensive, stable unit tests.
这是 Blazor 项目模板中的标准 Counter 页面。它有一个按钮,单击该按钮时,屏幕上的计数会增加。我们可以使用 Bunit 来测试此功能。以下是我们为它编写的测试:
public class CounterTests : BunitTestContext
{
[Fact]
public void CounterStartsAtZero()
{
// Arrange
var cut = RenderComponent<Counter>();
// Assert
cut.Find("p")
.MarkupMatches(
"<p role=\"status\">Current count: 0</p>");
}
[Fact]
public void ClickingButtonIncrementsCounter()
{
// Arrange
var cut = RenderComponent<Counter>();
// Act
cut.Find("button").Click();
// Assert
cut.Find("p")
.MarkupMatches(
"<p role=\"status\">Current count: 1</p>");
}
}
第一个测试方法 CounterStartsAtZero() 检查初始计数器值是否为零。它通过直接呈现 Counter 组件并检查相应的 HTML 是否包含 0 作为 count 值来实现这一点。
另一个测试方法 ClickingButtonIncrementsCounter() 检查在单击按钮时计数器是否递增。
总结
与其他等效技术相比,.NET Aspire 不仅允许开发人员相对容易地构建分布式编排应用程序,而且还使针对这些应用程序编写集成测试变得容易。
下次,我们将讨论保护 Aspire 应用程序。我们将讨论如何保护控制面板和托管应用程序,因此请关注此空间。
.NET Aspire Apps 集成测试的更多相关文章
- ASP.NET Core 中文文档 第五章 测试(5.2)集成测试
原文: Integration Testing 作者: Steve Smith 翻译: 王健 校对: 孟帅洋(书缘) 集成测试确保应用程序的组件组装在一起时正常工作. ASP.NET Core支持使用 ...
- iOS---The maximum number of apps for free development profiles has been reached.
真机调试免费App ID出现的问题The maximum number of apps for free development profiles has been reached.免费应用程序调试最 ...
- SQLite.Net-PCLUSING SQLITE IN WINDOWS 10 UNIVERSAL APPS
USING SQLITE IN WINDOWS 10 UNIVERSAL APPS 1.下载SQLite VSIX package并安装 http://sqlite.org/download.html ...
- QuanbenSoft Windows Runtime (Windows Store)Apps 应用及其框架总览
Parrot Simple audio repeater for language learners http://www.windowsphone.com/en-au/store/app/parro ...
- 小型文件数据库 (a file database for small apps) SharpFileDB
小型文件数据库 (a file database for small apps) SharpFileDB For english version of this article, please cli ...
- SharpFileDB - a file database for small apps
SharpFileDB - a file database for small apps 本文中文版在此处. I'm not an expert of database. Please feel fr ...
- 重新想象 Windows 8.1 Store Apps (81) - 控件增强: WebView 之加载本地 html, 智能替换 html 中的 url 引用, 通过 Share Contract 分享 WebView 中的内容, 为 WebView 截图
[源码下载] 重新想象 Windows 8.1 Store Apps (81) - 控件增强: WebView 之加载本地 html, 智能替换 html 中的 url 引用, 通过 Share Co ...
- SharePoint 2013技巧分享系列 - 隐藏Blog和Apps左侧导航菜单
企业内部网中,不需要员工创建Blog或者创建,安装SharePoint应用,因此需要在员工个人Web页面需要隐藏Blog或者Apps导航菜单, 其步骤设置如下: 该技巧适合SharePoint 201 ...
- 在线文档预览方案-office web apps续篇
上一篇在线文档预览方案-office web apps发布后收到很多网友的留言提问,所以准备再写一篇,一来介绍一下域控服务器安装,总结一下大家问的多的问题,二来宣传预览服务安装与技术支持的事情. 阅读 ...
- Quick Apps for Sharepoint小型BI解决方案
Quick Apps for Sharepoint介绍 Quick Apps for Sharepoint前身是Quest Webpart ,由企业软件开发商QuestSoftware开发,Quest ...
随机推荐
- 第22天:安全开发-PHP应用&留言板功能&超全局变量&数据库操作&第三方插件引用
#数据库操作-mysqli函数&增删改查 PHP函数:连接,选择,执行,结果,关闭等 参考:https://www.runoob.com/php/php-ref-mysqli.html 常用: ...
- CSP-S 2023 游记
CSP-S 2023 游记 Day 0 明天便是 CSP-S 第一轮了,考试前一天万万不能学什么太复杂,太深奥的东西,最好甚至不要过于强度的用脑,保持放空的轻松地状态,心中不要有压力才是最好的考前状态 ...
- 【赵渝强老师】史上最详细的PostgreSQL体系架构介绍
PostgreSQL是最像Oracle的开源数据库,我们可以拿Oracle来比较学习它的体系结构,比较容易理解.PostgreSQL的主要结构如下: 一.存储结构 PG数据存储结构分为:逻辑存储结构和 ...
- 记一次Razor Pages无法编译问题及解决
解决方案写在前面:更新Visual Studio及相关组件,本人版本自17.8.0更新至17.11.4 缘起于公司的一个业务接口,在有一些信息需要在应用内嵌的webview中展示,信息不少,涉及的前端 ...
- emmc寿命
EMMC器件寿命 1)先确认EMMC器件NAND FLASH类型,是MLC还是TLC,一般是TLC,器件手册标称1000-3000次,取平均值2000次作为评估: 2)在OS下查看EMMC器件当前使用 ...
- 墨天轮沙龙 | Proxima 刘方:阿里巴巴大规模向量检索实时服务化引擎 Proxima SE
导读 随着 AI 技术的广泛应用,以及数据规模的不断增长,向量检索也逐渐成了 AI 技术链路中不可或缺的一环. 在11月16日举办的[墨天轮数据库沙龙-向量数据库专场]邀请到阿里巴巴高级技术专家刘方, ...
- 未来“数”于你 | 墨天轮携手 Vertica 发布技术文章征集令,双重大奖蓄势待“发”
作为新一代数据分析平台,Vertica凭借高性能.高可用性以及混合模式部署的底层架构等特点,为国内电信.金融行业提供了较多的整体解决方案. 今天,Vertica 限时开放体验,同时,MacBook P ...
- 11-react使用props.children 处理父子组件之间的传值
// props.children 组件传值 import { Component } from "react" import reactDom from "react- ...
- day05-JDK的卸载与安装
JDK卸载 右键我的电脑--属性--高级系统设置--环境变量,找到JAVA_HOME安装目录,删除Java安装目录 删除JAVA_HOME 删除path下关于java的目录 cmd查看Java -ve ...
- Windows刷机-记录UltraSO工具安装错误
安装镜像刻录U盘工具UltralSO:UltraISO - ISO CD/DVD image creator, editor, burner, converter and virtual CD/DVD ...