.Net单元测试xUnit和集成测试指南(1)
引言
在现代化的软件开发中,单元测试和集成测试是确保代码质量和可靠性的关键部分。ASP.NET Core 社区内提供了强大的单元测试框架,xUnit 是其中之一,它提供了简单、清晰和强大的测试功能,编写单元测试有许多优点;有助于回归、提供文档及辅助良好的设计。下面几节我们来深入浅出探讨如何使用 xUnit 进行 ASP.NET Core 应用程序的单元测试和集成测试。
内容大纲:
xUnit 简介
xUnit.net是一个免费、开源、面向社区的.NET单元测试工具。由NUnit v2的原始发明者编写,xUnit.net是用于C#和F#(其他.NET语言可能也可以使用,但不受支持)的最新技术单元测试。xUnit.net可与Visual Studio、Visual Studio Code、ReSharper、CodeRush和TestDriven.NET一起使用。它是.NET基金会的一部分,并遵守其行为准则。其许可协议为 Apache 2(为 OSI 批准的许可协议)。
创建单元测试项目
在单元测试中通常要遵循AAA模式,也就是 Arrange、Act、Assert,这是一种常见的测试组织结构。
Arrange(准备): 在这个阶段,将设置测试的前提条件,初始化对象、设置输入参数等。简单讲就是准备测试环境,确保被测代码在正确的上下文中执行。Act(执行): 在这个阶段,会执行要测试的代码或方法。这是针对被测代码的实际调用或操作。Assert: 在这个阶段,会验证被测代码的行为是否符合预期。检查实际结果与期望结果是否一致,如果不一致则测试失败。
示例:
[Fact]
public void Add_EmptyString_ReturnsZero()
{
// Arrange
var stringCalculator = new StringCalculator();
// Act
var actual = stringCalculator.Add("");
// Assert
Assert.Equal(0, actual);
}
可读性是编写单元测试最重要的方面之一,在测试中分离这些操作 都明确地突出调用代码所需的依赖项、调用代码的方式以及尝试断言的内容,让测试尽可能具有可读性。
好了理解了这个核心概念我们可以先创建项目一步步的练习了。
用 VS 创建单元测试项目

在项目创建完之后我们可以简单浏览一下 xUnit 单元测试项目装了那些 nuget 依赖,做到对项目有个简单的了解
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>
下面我们创建一个简单的数据计算类。
- 创建数学计算类
public class MathCalculator
{
public int Add(int a, int b)
{
return a + b;
}
}
- 创建数据计算测试类
public class MathCalculatorTests
{
[Fact]
public void Add_TwoNumbers_ReturnSum()
{
// Arrange
var calculator = new MathCalculator();
// Act
var result = calculator.Add(3, 5);
// Assert
Assert.Equal(8, result);
}
}
测试一下,测试类库右键->运行测试

可以看到 我们的单元测试通过。
单元测试命名规范
本着代码自文档的原则,测试的名称建议应包括三个部分:
- 要测试的方法的名称。
- 测试的方案。
- 调用方案时的预期行为。
示例
[Fact]
public void Add_TwoNumbers_ReturnSum()
{
// Arrange
var calculator = new MathCalculator();
// Act
var result = calculator.Add(3, 5);
// Assert
Assert.Equal(8, result);
}
要测试的方法名称是 MathCalculator 中的 Add 方法,测试的方案是传两个数,预期是返回两数之和 按照上面的测试名称的命名规则可以命名为Add_TwoNumbers_ReturnSum。
单元测试最佳命名规范应该包括三个关键部分:要测试的方法的名称、测试的场景,以及调用该场景时的预期行为。良好的命名标准能清晰表达测试意图,提供有效文档,便于他人理解代码行为和快速定位问题。
将方法标记为测试方法在
xUnit中有两个属性,Fact和Theory
Fact 属性
在方法上我们看到有一个 Attribute [Fact] ,[Fact] 属性是 xUnit 中最基本的测试属性之一,用于标记一个方法作为一个无需参数且不返回任何内容的测试方法。被标记为 [Fact] 的方法将会被 xUnit 框架识别并执行.
Theory 属性
Theory 属性用于标记一个测试方法,该方法可以接受参数并运行多次,每次运行时使用不同的参数值。Theory 属性通常用于数据驱动测试,允许在同一个测试方法中使用不同的输入数据进行测试.
InlineData 属性
[InlineData] 属性指定这些输入 Theory 标记的测试方法的参数值。
示例:
[Theory]
[InlineData(-1)]
[InlineData(0)]
[InlineData(1)]
public void IsPrime_ValuesLessThan2_ReturnFalse(int value)
{
var result = _primeService.IsPrime(value);
Assert.False(result, $"{value} should not be prime");
}
InlineData适用于静态、硬编码的测试数据集合,适合于简单且固定的测试场景。
MemberData 属性
MemberData 属性是 xUnit 中用于数据驱动测试的一种方式,它允许从一个字段、属性或方法中获取测试数据,并将这些数据传递给测试方法进行多次测试。通过 MemberData 属性,可以更灵活地管理和提供测试数据,适用于需要动态生成测试数据的情况。
使用方式
- 标记测试方法:使用 [Theory] 属性标记测试方法,以便接受从 MemberData 属性提供的测试数据。
- 准备测试数据:创建一个公共静态字段、属性或方法,该字段、属性或方法返回一个 IEnumerable<object[]> 对象,其中每个 object[] 对象代表一组测试数据。
- 传递测试数据:在 MemberData 属性中指定要使用的数据源,从而将数据传递给测试方法。
示例
public static IEnumerable<object[]> GetComplexTestData()
{
yield return new object[] { 10, 5, 15 }; // 测试数据 1
yield return new object[] { -3, 7, 4 }; // 测试数据 2
yield return new object[] { 0, 0, 0 }; // 测试数据 3
// 可以根据需要继续添加更多的测试数据
}
[Theory]
[MemberData(nameof(GetComplexTestData))]
public void Add_TwoNumbers_ReturnsSumofNumbers01(int first, int second, int sum)
{
// Arrange
var calculator = new MathCalculator();
// Act
var result = calculator.Add(first, second);
// Assert
Assert.Equal(sum, result);
}
MemberData 适用于动态、灵活的测试数据集合,适合于需要从外部源动态获取测试数据的情况。

自定义属性
除了上面提到的 InlineData和MemberData 之外还可以有更加灵活的方式继承DataAttribute实现自定义的Attribute。
我们来做一个实现和上面一样的需求
- 实现 Custom Attribute
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class CustomDataAttribute : DataAttribute
{
private readonly int _first;
private readonly int _second;
private readonly int _sum;
public CustomDataAttribute(int first, int second, int sum)
{
_first = first;
_second = second;
_sum = sum;
}
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
yield return new object[] { _first, _second, _sum };
}
}
- 用例
[Theory]
[CustomData(1, 2, 3)]
[CustomData(2, 3, 5)]
public void Add_TwoNumbers_ReturnSum03(int num1, int num2, int expectedSum)
{
// Arrange
var calculator = new MathCalculator();
// Act
var result = calculator.Add(num1, num2);
// Assert
Assert.Equal(expectedSum, result);
}
自定义属性相较于使用 InlineData 和 MemberData 有以下优势:
灵活性:自定义属性允许您实现更复杂的逻辑来动态生成测试数据,可以从不同数据源中获取数据,实现更灵活的数据驱动测试。
重用性:通过自定义属性,您可以将相同的测试数据逻辑应用于多个测试方法,提高测试代码的重用性和可维护性。
可扩展性:自定义属性可以根据需求进行定制和扩展,适应不同的测试场景和数据需求,使得测试数据的生成更具灵活性。
可读性:通过自定义属性,可以使测试代码更具可读性和表达力,更清晰地表达测试数据的来源和意图。
尽管使用 InlineData 和 MemberData 可以满足大多数简单的测试数据需求,但当需要更复杂的数据生成逻辑、数据源、或者对测试数据进行处理时,使用自定义属性会更具优势,能够更好地满足个性化的测试需求。
在测试中应避免逻辑
[Theory]的出现就是为了避免我们在单元测试时编写一些额外的逻辑,造成测试之外的一些错误。
编写单元测试时,请避免手动字符串串联、逻辑条件(例如 if、while、for 和 switch)以及其他条件。
错误示范:
[Fact]
public void Add_TwoNumbers_ReturnsSumofNumbers02()
{
// Arrange
var calculator = new MathCalculator();
var testData = new List<(int, int, int)>
{
(1, 2, 3),
(2, 3, 5),
(3, 4, 7)
};
// Act & Assert
foreach (var (first, second, sum) in testData)
{
var result = calculator.Add(first, second);
Assert.Equal(sum, result);
}
}
此处用了 forEach 循环来批量断言,违反了单元测试的最佳实践。
测试中应避免逻辑的好处是:
- 降低在测试中引入 bug 的可能性。
- 专注于最终结果,而不是实现细节。
ITestOutputHelper 控制台输出
在 xUnit 中我们利用 Console.WriteLine输出时发现什么也不会显示,在 xUnit 单元测试项目中我们需要利用ITestOutputHelper。
ITestOutputHelper是 xUnit 中的一个接口,用于在单元测试中输出信息。通过 ITestOutputHelper,您可以在测试运行时将调试信息、日志信息等输出到测试结果中,方便调试和查看测试过程中的输出信息。
调试
再要测试的方法上右键选择调试测试,或者点击方法上面的小点

最后
本篇文章简单的讲了单元测试的基础知识,让大家先对单元测试有个基本的概念,这些用在具体的项目中显然是不够的,后面的章节我们聊一下 TDD,Fake 管理,Log 日志输出,单元测试覆盖率,WebApi 的集成测试,DependencyInjection,Bogus,还有 Devops 的单元测试等知识。
.Net单元测试xUnit和集成测试指南(1)的更多相关文章
- Mokito 单元测试与 Spring-Boot 集成测试
Mokito 单元测试与 Spring-Boot 集成测试 版本说明 Java:1.8 JUnit:5.x Mokito:3.x H2:1.4.200 spring-boot-starter-test ...
- 单元测试-xUnit总结
xUnit总结 什么是xUnit xUnit.net是针对.NET Framework的免费,开源,以社区为中心的单元测试工具. 自动化测试的优点 可以频繁的进行测试 可以在任何时间进行测试,也可以按 ...
- MVC 单元测试xUnit初探
对于.NET项目 Web Api的业务逻辑后台开发[特别是做Web Api接口]而言,编写单元测试用例,会极大的减轻代码帮助与运行的方式.然而使用测试框架,相对于自带的,我更加推荐是用xUnit.ne ...
- ASP.NET Core 入门(3)(单元测试Xunit及Shouldly的使用)
一.本篇简单介绍下在ASP.NET Core项目如何使用单元测试,例子是使用VS自带的Xunit来测试Web API接口,加上一款开源的断言工具Shouldly,方便写出更简洁.可读行更好的测试代码. ...
- .Net core--创建一个单元测试xUnit
创建一个xUnit项目 webApi.test 创建之后会有一个默认的[Fact] (测试的标准格式) [Fact] public void TestEqual() { int a = 10, b ...
- .NET 程序集单元测试工具 SmokeTest 应用指南
Smoke Test(冒烟测试),也称Regression Test(回归测试),是对软件的安装和基本功能的测试.一般地我们使用脚本来实现Smoke Test的自动化,可借用虚拟机的snapshot机 ...
- 如何在ASP.NET 5和XUnit.NET中进行LocalDB集成测试
今天继续昨天的话题--单元测试,不过是在ASP.NET 5中的单元测试. 在当前的Visual Studio 2015 CTP6中,MSTest是不支持对ASP.NET 5项目进行单元测试的.因而,要 ...
- .NET Core 3.0 单元测试与 Asp.Net Core 3.0 集成测试
单元测试与集成测试 测试必要性说明 相信大家在看到单元测试与集成测试这个标题时,会有很多感慨,我们无数次的在实践中提到要做单元测试.集成测试,但是大多数项目都没有做或者仅建了项目文件.这里有客观原因, ...
- Unity3d官方测试插件学习-单元测试,集成测试
2016/11/27更新:官方的测试工具有许多问题,我修改了一个版本 https://git.oschina.net/Hont/UnitTest_Modifyed 支持切场景,异常不失败等 其实Uni ...
- Tessy — 嵌入式软件单元测试/ 集成测试工具
Tessy 源自戴姆勒- 奔驰公司的软件技术实验室,由德国Hitex 公司负责全球销售及技术支持服务,是一款专门针对嵌入式软件进行单元/ 集成测试的工具.它可以对C/C++ 代码进行单元.集成测试,可 ...
随机推荐
- 循环掌控:深入理解C语言循环结构,高效实现重复性任务
欢迎大家来到贝蒂大讲堂 养成好习惯,先赞后看哦~ 所属专栏:C语言学习 贝蒂的主页:Betty's blog 引言 前面贝蒂带大家了解了选择结构,今天就来为大家介绍循环结构,也就是我们熟悉的while ...
- day03--实际操作演示linux系统挂载过程
# 第一步骤: 拥有一个存储设备-光驱,使光驱加载光盘 # 第二步骤: 在linux系统中找到光驱设备 ls -l /dev/cdrom # 第三步骤: 需要将存储设备进行 挂载 挂载命令语法格式: ...
- 【LeetCode双指针】合并两个有序数组,从后向前遍历
合并两个有序数组 https://leetcode.cn/problems/merge-sorted-array/ 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m ...
- Golang标准库之bytes介绍
本次主要介绍golang中的标准库bytes,基本上参考了 字节 | bytes .Golang标准库--bytes 文章. bytes库主要包含 5 大部分,即: 常量 变量 函数 Buffer R ...
- 游戏H5引擎Canvas屏幕自适应CSS代码
canvas.style = `touch-action: none; width:${ width }px; height:${ height }px; cursor: inherit;`;
- 手把手教你蜂鸟e203协处理器的扩展
NICE协处理器 赛题要求: 对蜂鸟E203 RISC-V内核进行运算算子(譬如加解密算法.浮点运算.矢量运算等)的扩展,可通过NICE协处理器接口进行添加,也可直接实现RISC-V指令子集(譬如 ...
- mysql-编写脚本-批量插入数据
一.代码 -- 报工设置,添加数据 set @org_id = '租户id'; set @created_user = 'yike'; set @updated_user = 'yike'; set ...
- 独家消息:阿里云悄然推出RPA云电脑,已与多家RPA厂商开放合作
独家消息:阿里云悄然推出RPA云电脑,已与多家RPA厂商开放合作 RPA云电脑,让RPA开箱即用算力无限? 文/王吉伟 这几天,王吉伟频道通过业内人士获得独家消息,阿里云近期推出了一个名为「RPA云电 ...
- linux压缩文件并排除指定目录
今天要在linux上打包一个项目另作他用,但是项目图片都是放本地服务器的,整个项目打包好后有2G多下载十分费时.项目中的图片我们可以不要,所以压缩的时候要排除图片目录. 具体命令如下: // 参数说明 ...
- docker 系列
docker 系列 目录 docker 系列 一. docker 定义 1 nameSpnce 命名空间 2 cgroup 控制组 3 为什么使用容器 二. docker 安装 三 .docker 初 ...