如何测试 ASP.NET Core Web API

在本文中,我们将研究如何测试你的 ASP .NET Core 2.0 Web API 解决方案。我们将了解使用单元测试进行内部测试,使用全新的 ASP .NET Core 的集成测试框架来进行外部测试。

本文要点

  • 正确理解并使用单元测试和你的 ASP .NET Core Web API 解决方案一样重要。

  • 了解并使用模拟数据进行单元测试可以帮助你获得稳定的测试场景。

  • 为 ASP .NET Core Web API 解决方案在 .NET Core 2.1 中创建模拟数据项目。

  • 了解并设置集成测试来从外部测试 API,这样可以帮助完整测试 ASP .NET Core 2.1 Web API 解决方案。

.NET Core 最初是在 2016 年发布的,随着 .NET Core 2.0 的发布,微软拥有了下一个通用、模块化、跨平台和开源的平台主版本。.NET Core 已经创建了许多 API,在当前版本的 .net 框架中均可用。它最初是为下一代 ASP .NET 解决方案而创建的,但现在成了许多其他场景的驱动和基础,包括物联网、云计算和下一代移动解决方案。在本系列文章中,我们将探讨 .NET Core 的一些好处,以及它如何不仅能使传统的 .NET 开发人员受益,还能使所有需要为市场带来健壮、高效和经济的解决方案的技术人员受益。

在使用 ASP .NET Core 2.1 Web API 构建并开发一组丰富的 API 的时候,需要记住这只是实现稳定且高效的解决方案的第一步。为你的解决方案提供一个稳定的环境是非常重要的。获得优秀的解决方案不仅需要完整地构建 API,还需要严格地测试你的 API,保证用户有良好的体验。

这篇文章是我之前为 InfoQ 写的《针对 ASP.NET Core Web API 的先进架构》的后续文章。请放心,你不需要阅读另外一篇文章,就可以从这篇文章了解到如何从测试中获得好处,但读一下那篇文章可以帮助你更多了解我是如何构建我讨论的解决方案的。在过去的几年里,我花了大量时间来思考在为客户构建 API 时进行测试。了解 ASP .NET Core 2.1 Web API 的架构,可以帮助拓宽你的了解。

本篇文章中的例子的解决方案和所有代码都可以在我的GitHub 库中找到。

ASP .NET Core Web API 快速入门

让我们一起来快速了解一下 .NET 和 ASP .NET Core。ASP .NET Core 是 Microsoft 创造的全新 Web 框架,以便摆脱自 ASP .NET 1.0 以来一直存在的遗留技术。ASP .NET Core 2.1 摆脱这些遗留依赖,从头开始开发框架,因此可以给开发人员提供更高的性能,并且它是为跨平台执行而构建的。

什么是单元测试?

对有些人来说,测试你的软件可能是一个新的概念,但是它很简单。我们从单元测试开始。维基百科对于它的严格定义是“是一种软件测试方法,对源代码的独立单元、一组或多组计算机程序模块以及相关的控制数据、使用程序和操作程序都进行测试,以了解它们是否适合使用”。我比较喜欢的是一个外行的解释,单元测试是保证在你添加了新的功能或进行了缺陷修复之后,你的解决方案中的代码能像预期一样执行。我们测试了一个简单的代码示例,来保证它符合我们的预期。让我们来看一下单元测试示例:

复制代码

[Fact]

public async Task AlbumGetAllAsync()

{

// Arrange


// Act

var albums = await _repo.GetAllAsync();


// Assert

Assert.Single(albums);

}

优秀的单元测试有三个部分组成。第一个是Arrange的部分,用来设置测试中可能需要的任何资源。在上面的例子中,我没有进行任何设置,所以 Arrange 的部分是空的 (但我还是为它保留了注释)。第二个部分是Act的部分,用来执行测试的部分。在我们的例子中,我调用数据库中的专辑实体类型,返回当前使用的库中的数据源完整的专辑实体。单元测试的最后一个部分是Assert的部分,用来验证待测试的操作是否正确。对于该测试,我检验是否从数据库中返回了一个专辑。

在本文中,我会使用 xUnit 工具进行单元测试。xUnit 是 .NET Framework 和现在的 .NET Core 的开源包。我们需要 .NET Core 版本的 xUnit,在你安装 .NET Core 2.1 SDK 的时候就已经直接获得了。你可以通过 .NET Core cli 指令 dotnet test,或是通过你喜欢的 IDE(如 Visual Studio 2017、Visual Studio Code 或 JetBrain 的 Rider.)中的项目模板来创造新的单元测试项目。

640?wx_fmt=jpeg

图 1:在 Visual Studio 2017 中创建新的单元测试项目

现在,让我们深入到测试你的 ASP .NET Core Web API 解决方案的单元测试中来。

Web API 要单元测试一些什么?

我非常支持使用单元测试来为你的客户保证稳定和健壮的 API。但我清楚地知道要如何使用单元测试,知道要测试什么东西。我相信,你要恰到好处地对解决方案进行单元测试,而不要做多余的测试。这是什么意思呢?可能我的观点会引发很多评论,但是我不太注重要 100% 覆盖你的测试。我是否认为我们需要能覆盖 API 解决方案重要部分的测试,单独隔开每个区域,保证每个代码段都是正确的?当然!我会这么做,这也是我想要讨论的。

由于我们的示例 Chinook.API 很小,并且可以通过集成测试来完成测试(本文稍后讨论),我发现在 Domain 和 Data 项目中我最关注单元测试。我不会详细讨论单元测试的方法(因为这个问题超出了本文讨论的范围)。我想要让你不依赖于生产数据库的数据,从而在 Domain 和 Data 项目中进行更多的测试。这是我们下一个要讨论的问题,模拟数据和对象。

为什么在单元测试中使用模拟数据和对象?

我们已经讨论了为什么我们要进行单元测试,单元测试一些什么内容。接下来,了解如何准确地单元测试 ASP .NET Core Web API 解决方案是非常重要的。数据是测试 API 的重点。测试可预测的数据集是非常重要的。这就是为什么我不推荐使用生产数据或者其他可能随着时间根据你的了解和认知会改变的数据。我们需要稳定的数据集来保证所有单元测试的运行,确保代码段之间的测试是相同的。比如说,在我测试 Chinook.Domain 项目的时候,我想要得到 ID 是 42 的专辑,我需要保证它的确存在,并拥有类似专辑名称这样的细节,和一条艺术家数据有关联关系。我还希望确保在我从数据源中得到一系列专辑时,大小满足我编写的单元测试。

许多业内人士使用“模拟数据”这一术语来表示这一类数据。有很多为单元测试产生模拟数据的方法,我希望你能创造出尽可能“真实”的数据集。你给测试创造的数据越好,测试效果也会越好。我会建议你确保数据没有隐私问题,不包含公司或客户的个人数据或敏感数据。

要满足我们对于干净、稳定数据的需求,我单独创建了一个项目,封装了单元测试项目的模拟数据。让我们称其为 Chinook.MockData(就像你能在示例中看到的一样)。我的 MockData 项目几乎和 Chinook.Data 项目相同。它们都拥有相同数量的数据库,都和相同的接口依附。我希望 MockData 项目存储在依赖注入 (DI) 容器中,这样 Chinook.Domain 项目就可以像连接到了生产数据源那样得到使用。这就是为什么我很喜欢依赖注入。它可以帮助我通过配置切换 Data 项目,而不需要做任何代码变更。

集成测试:什么是针对 Web API 的新测试?

在我们为 ASP .NET Core Web API 解决方案执行并验证了单元测试之后,我们要看一个完全不同类型的测试。我希望单元测试可以验证并确保对解决方案内部组件的期望。当我们对内部测试的质量满意的时候,我们需要从外部接口进行 API 测试,这就是我们所说的集成测试。

集成测试需要在所有的组件完成的时候编写并执行,所以你的 API 可以通过正确的 HTTP 响应来验证。单元测试时测试的是单独隔离开的代码段,而集成测试时测试的是 HTTP 端点上每个 API 的整体逻辑。测试将会遵循 API 的完整工作流,从 API 项目的控制器到域项目管理器,最后到 Data 项目的库(返回来响应)。

创造集成测试项目

要使用你现有的测试知识,集成测试功能是基于现有的单元测试库的。我将使用 xUnit 来创造我的集成测试。在我们创建了名为 Chinook.IntegrationTest 的新 xUnit 测试项目之后,我们需要添加合适的 NuGet 包。将 Microsoft.AspNetCore.TestHost 包添加到 Chinook.IntegrationTest 项目中来。这个包中包含了执行集成测试的资源。

640?wx_fmt=jpeg

图 2:添加 Microsoft.AspNetCore.TestHost NuGet 包

接下来,我们可以创建第一个集成测试来从外部验证我们的 API。

创建第一个集成测试

要想进行我们解决方案中的所有 API 外部测试,我要创建一个名为 API 的文件夹,其中包含了测试。我还需要在 API 域中给每个实体类型创建新的测试类。我们的首个集成测试将会覆盖专辑实体类型。

在 API 文件夹中创建新的类 AlbumAPITest.cs。之后我们会在文件中添加如下的命名空间。

复制代码

using Xunit;

using Chinook.API;

using Microsoft.AspNetCore.TestHost;

using Microsoft.AspNetCore.Hosting;

640?wx_fmt=jpeg
图 3:使用指令进行集成测试

现在我们要使用 TestServer 来设置类,使用 HttpClient 来执行测试。我们需要名为 _client,类型为 HttpClient 的私有变量,它是基于在 AlbumAPITest 类中的构造函数中初始化的 TestServer 而创建的。TestServer 是小型 web 服务器的包装器,是基于 Chinook.API Startup 和需要的开发环境创建的。在这个例子中,我使用开发环境。我们现在具备了运行 API 的 web 服务器,以及了解如何在 TestServer 中调用 API 的客户端。我们可以开始编写集成测试代码了。

640?wx_fmt=jpeg

图 4:我们第一个集成测试,获得所有的专辑

除了构造函数代码之外,图 4 中还展示了第一个集成测试的代码。AlbumGetAllTestAsync 方法将会测试验证从 API 获取所有专辑的调用。就像之前讨论的单元测试一样,集成测试的逻辑也是用了 Arrange、Act 和 Assert。我们首先创建 HttpRequestMessage 对象,其中 HTTP 作为 InlineData 注释中的变量而提供,URI 部分表示对于所有的专辑的调用 (“/api/Album/”)。之后我们会让 HttpClient _client 发送 HTTP 请求,最后,我们会检查验证 HTTP 响应是否满足我们的期望,在本例中是 200。图 4 中我展示了两种检验 API 调用的方法。你可以使用其中任意一种,但我更喜欢第二种方法,因为它允许我用相同的模式来检验对特定 HTTP 响应代码的响应。 

response.EnsureSuccessStatusCode();

Assert.Equal(HttpStatusCode.OK, response.StatusCode);

我们还可以创建需要从 API 测试特定实体键的集成测试。对于这类测试,我们需要在 InlineData 注释中添加额外的值,将会通过 AlbumGetTestAsync 方法参数传递。我们的新测试会使用相同的逻辑,并使用和之前测试一样的资源,但是我们为 HttpRequestMessage 对象在 API URI 端中传递实体键。你可以在图 5 中查看代码。

640?wx_fmt=jpeg

图 5:专辑的第二个集成测试

在你为测试 API 创造了所有集成测试之后,需要通过 Test Runner 来运行它们,并保证它们全部通过。你创建的所有测试也可以在 DevOps 持续集成(CI)过程中执行,这样可以在整体开发和部署过程中测试你的 API。现在需要有执行路径保证你的 API 在开发、质量保证和部署阶段都得到测试和维护,让你的 API 使用者拥有良好体验的同时不发生意外情况。

640?wx_fmt=jpeg

图 6:在 Visual Studio 2017 中运行集成测试

结论

拥有良好设计的一套测试计划,使用单元测试来做内部测试,使用集成测试来验证 API 调用就和开发 ASP .NET Core Web API 阶段创建架构是一样重要的。

本文作者

Chris Woodruff (Woody) 拥有密歇根州立大学工程学院的计算机科学学位。Woody 已经开发和架构软件解决方案超过 20 年,并且曾经致力于许多不同的平台和工具。他是一个社区领袖,为 GRDevNight、GRDevDay、West Michigan Day of .NET 和 CodeMash 之类的活动贡献过力量。他还帮助把广受欢迎的 Give Camp 活动带到西密歇根,那里的技术专业人士提供他们的时间和发展专业知识,以帮助当地的非营利组织。作为一个演讲者和播客作者,Woody 演讲和讨论过很多话题,包括数据库设计和开源。他在 Visual C#、数据平台和 SQL 方面一直是微软的 MVP,并在 2010 年被公认为全球最优秀的 20 个 MVPs 之一。Woody 是 JetBrains 的开发者,并且在北美推广 .NET、.NET Core 和 JetBrains 的产品。

.NET Core 最初是在 2016 年发布的,随着 .NET Core 2.0 的发布,微软拥有了一个通用、模块化、跨平台和开源的平台主版本。.NETCore 已经创建了许多 API,在当前版本的 .net 框架中均可用。它最初是为下一代 ASP .NET 解决方案而创建的,但现在成了许多其他场景的驱动和基础,包括物联网、云计算和下一代移动解决方案。在本系列文章中,我们将探讨 .NET Core 的一些好处,以及它如何不仅能使传统的 .NET 开发人员受益,还能使所有需要为市场带来健壮、高效和经济的解决方案的技术人员受益。

原文地址:https://www.infoq.cn/article/4Z59Jy7ptKNERdv-o8Qp


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

640?wx_fmt=jpeg


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/317901.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[帝皇杯day 1] [NOIP2018模拟赛]小P的loI(暴力+素筛),【NOIP模拟赛】创世纪(贪心),无聊的数对(线段树)

文章目录T1:小P的loltitlesolutioncodeT2:创世纪titlesolutioncodeT3:无聊的数对titlesolutioncodeT1:小P的lol title solution 此题非常水… 先用素数筛,筛出[1,n][1,n][1,n]中的质数 质数越小,倍数的分…

P6144 [USACO20FEB]Help Yourself P(DP+线段树)

P6144 [USACO20FEB]Help Yourself P 将线段按照了 \(r\) 排序,设右端点为 \(r\) 的答案为 \(f_r\),发现这样转移非常困难。 \(\color{yellow}{\bigstar\texttt{Trick}}\):区间覆盖的题要按照左端点排序,记右端点为 \(r\) 时的答案…

Desert King POJ - 2728

题意: 给定N个平面上的点的坐标和它们的权值,任意两点之间的边的价值是它们的距离,费用是两点权值之差的绝对值,求该图的一棵生成树,使得该树所有边的费用之和与价值之和的比值最小(只需求这个比值即可&am…

洛谷P3338:力(FFT)

传送门 解析 算是比较适合的FFT入门题了吧 一个重要的trick: 当函数无法表示成卷积时,可以把函数翻转过来 然后调一调就又是卷积了 一个重要的注意事项是FFT的lim一定是两多项式相乘结果多项式的项数! 即使后面的项根本没有用也一样 其他的…

CF650E-Clockwork Bomb【并查集】

正题 题目链接:https://www.luogu.com.cn/problem/CF650E 题目大意 给出nnn个点的两棵树,你每次可以选择第一棵树上的一条边改成另一条边,但是改完之后必须还是一棵树,求最少的步数把第一棵树改成第二棵树。 1≤n≤51051\leq n\leq 5\times…

[2-sat专练]poj 3683,hdu 1814,hdu 1824,hdu 3622,hdu 4115,hdu 4421

文章目录Priest Johns Busiest DaycodePeaceful CommissioncodeLets go homecodeBomb GamecodeEliminate the ConflictcodeBit MagiccodePriest John’s Busiest Day 题目 司仪必须在婚礼开始或结束时出现,考虑把第iii场婚礼拆成两个点 iii:表示司仪在婚…

.NET Core中的一个接口多种实现的依赖注入与动态选择

最近有个需求就是一个抽象仓储层接口方法需要SqlServer以及Oracle两种实现方式,为了灵活我在依赖注入的时候把这两种实现都给注入进了依赖注入容器中,但是在服务调用的时候总是获取到最后注入的那个方法的实现,这时候就在想能不能实现动态的选…

Sightseeing Cows POJ - 3621

题意: L个点,P边的点边带权的有向图,求一个环点权和与边权和比值的最大值。 题解: 01分数规划判负环 详细看这里 还是套用01分数规划模型,点权为value[i],边权为cost[u],一个环为C,问题要求最大化 最…

CF559E Gerald and Path(DP)

CF559E Gerald and Path 设 \(dp(i,p)\) 表示完成前 \(i\) 条线段的覆盖,最右端位于 \(p\) 点的最大收益。 转移?向下一条线段转移时加上他们中间的距离?发现这样没有办法统计 \(p\) 点以前的空位了! \(\color{yellow}{\bigstar\t…

多项式乘法:练习总结

文章目录前言力代码礼物代码差分和前缀和代码开拓者的知识代码总结前言 这两天由于国庆集训全是阴间的生成函数,所以就学了一点点相关的内容 其实就学了个FFT和NTT 也算是点开了一个小小的技能点吧 进入多项式才发现里面世界的广阔 然而由于这玩意没有一个是NOIP考…

CF1444C-Team-Building【可撤销并查集】

正题 题目链接:https://www.luogu.com.cn/problem/CF1444C 题目大意 给出nnn个点mmm条边的一张图,总共kkk个颜色,每个点有一个颜色。 询问有多少无序颜色对(x,y)(x,y)(x,y)满足x≠yx\neq yx​y且颜色为xxx或yyy的点构成的生成子图是一个二分图。 1≤…

C# Memory Cache 踩坑记录

背景前些天公司服务器数据库访问量偏高,运维人员收到告警推送,安排我团队小伙伴排查原因.我们发现原来系统定期会跑一个回归测试,该测运行的任务较多,每处理一条任务都会到数据库中取相关数据,高速地回归测试也带来了高频率的数据库读取.解决方案1我们认为每个任务要取的数据大…

模板:无旋treap

文章目录前言操作合并分裂插入删除查找第k大查询x的排名前驱后继完整代码所谓无旋treap,就是不带旋转的treap 前言 现在“理论上”我会四种平衡树了 之前说无旋treap功能弱,是我狗眼看银低了qwq 这玩意区间上该搞的也能搞 而且确实是非常的 好写&#x…

[贪心专题]CF549G,CF351E,CF226D,CF1276C,CF1148E,CF798D

文章目录T1:CF1276C Beautiful RectangletitlesolutioncodeT2:CF226D The tabletitlesolutioncodeT3:CF549G Happy LinetitlesolutioncodeT4:CF798D Mike and distributiontitlesolutioncodeT5:CF351E Jeff and Permut…

51nod1766-树上的最远点对【结论,线段树】

正题 题目链接:http://www.51nod.com/Challenge/Problem.html#problemId1766 题目大意 给出nnn个点的一棵树,mmm次询问给出两个区间,要求在两个区间中各选一个点使得他们之间距离最大。 1≤n,m≤1051\leq n,m\leq 10^51≤n,m≤105 解题思路 结论就是两…

P3385 【模板】负环

P3385 【模板】负环 题意: 给定一个 n 个点的有向图,请求出图中是否存在从顶点 1 出发能到达的负环。 负环的定义是:一条边权之和为负数的回路。 题解: 先说结论: 判断给定的有向图中是否存在负环。 利用 spfa 算…

LOJ洛谷P1248加工生产调度(贪心、Johnson 法则)

解析 和国王游戏一样的做法 容易写出cmp函数的依据&#xff1a; min(u.a,v.b)<min(u.b,v.a)但是这个题的比较函数有一个取min的操作 这个东西会有一个问题&#xff1a;不满足不可比性的传递性 通俗的说&#xff0c;xy,yz,但是x不一定z 比如说如果y的a和b都很小&#xff0c…

【数位DP】CF 54C,509C,431D,628D,855E,1245F,95D

这一次有题解了&#xff01;&#xff01;T1&#xff1a;CF54C First Digit LawtitlesolutioncodeT2&#xff1a;CF509C Sums of DigitstitlesolutioncodeT3&#xff1a;CF431D Random TasktitlesolutioncodeT4&#xff1a;CF628D Magic NumberstitlesolutioncodeT5&#xff1a;…

CF1007E Mini Metro(DP 化散为整,将状态压缩)

CF1007E Mini Metro 有 \(N\) 个站台&#xff0c;从左往右编号为 \(1,2,\cdots,n\)&#xff0c;每个站台初始时(\(0\) 时刻)有 \(a_i\) 个人&#xff0c;从 \(0\) 时刻初开始游戏。 每个时刻&#xff0c;会依次发生如下事件&#xff1a; 你可以选择召唤 \(0\) 辆、一辆或多辆向…

AT2070-[ARC061D]3人でカードゲーム/Card Game for Three【计数,组合数学】

正题 题目链接:https://www.luogu.com.cn/problem/AT2070 题目大意 有三堆卡牌各有n,m,kn,m,kn,m,k张&#xff0c;每张上写了a/b/ca/b/ca/b/c&#xff0c;对于第1/2/31/2/31/2/3堆卡牌。然后开始从第一堆拿牌&#xff0c;然后根据拿到的牌在对应的堆拿牌。 如果到一堆拿牌时…