xUnit总结--学习笔记

article/2025/10/20 12:26:35

xUnit.net是针对.NET Framework的免费,开源,以社区为中心的单元测试工具。

自动化测试的优点#

  • 可以频繁的进行测试
  • 可以在任何时间进行测试,也可以按计划定时进行,例如:可以在半夜进行自动化测试
  • 比人工测试速度快
  • 可以更快速地发现错误
  • 基本上是非常可靠的
  • 测试代码与生产代码紧密结合
  • 使得开发团队更具有幸福感

自动化测试的分类#

单元测试可以测试某个类或方法,具有较高的深度,对应用的功能覆盖面很小。
集成测试有更好的广度,可以测试web资源,数据库资源等。
皮下测试在web中针对controller下的节点测试。
UI测试是对应用的界面功能测试。
实际上常用的是单元测试和集成测试。

是测试行为还是测试私有方法#

一般是针对类的Public方法进行测试,也就是对行为进行测试,如果是私有方法需要改变修饰符才能测试

xUnit.Net特点:#

  • 支持多平台/运行时
  • 并行测试
  • 数据驱动测试
  • 可扩展

xUnit支持的平台:#

.Net Framework
.Net Core
.Net Standard
UWP
Xamarin

官网:
https://xunit.net

测试工具:

VS自带的测试浏览器(右键测试或者ctrl+r,t)
resharper,
cmd命令行(.net cli): dotnet testdotnet test --help

简单的例子:

1、在VS中创建一个解决方案,在创建一个.net core 类库,添加一个Calculator类:

namespace Demo
{public class Calculator{public int Add(int x,int y){return x + y;}}
}

2、在同一解决方案,创建一个xUnit测试项目:DemoTest,针对项目测试,一般是项目名+Test命名测试项目。创建一个类:CalculatorTests:

public class CalculatorTests
{[Fact]public void ShouldAddEquals5() //注意命名规范{//Arrangevar sut = new Calculator(); //sut-system under test,通用命名//Actvar result = sut.Add(3, 2);//AssertAssert.Equal(5, result);}
}

3、

  1. 运行测试(任意一种方法):
    1. 通过vs自带的测试资源管理器,找到测试项目,选择运行;

测试行为还是测试私有方法:

public void IncreaseHeartBeatRate()
{HeartBeatRate = CalculateHeartBeatRate() + 2
}private int CalculateHeartBeatRate()
{var random = new Random();return random.Next(1,100);
}

    1. 通过在ShouldAddEquals5方法上,右键选择运行测试或者快捷键(ctrl+r,t)
    1. 通过cmd,在测试项目目录运行dotnet test

    1. resharper(没有安装,太耗费内存)

测试的三个阶段:AAA#

Arrange: 在这里做一些先决的设定。例如创建对象实例,数据,输入等。
Act: 在这里执行生产代码并返回结果。例如调用方法或者设置属性。
Assert:在这里检查结果,会产生测试通过或者失败两种结果。


Assert

Assert基于代码的返回值、对象的最终状态、事件是否发生等情况来评估测试的结果
Assert的结果可能是Pass或者Fail
如果所有的asserts都通过了,那么整个测试就通过了。
如果任何assert 失败了,那么结果就失败了。

一个test里应该有多少个asserts

  1. 一种简易的做法是,每个test方法里面只有一个assert.
  2. 而还有一种建议就是,每个test里面可以有多个asserts,只要这些asserts都是针对同一个行为。
    xUnit提供了以下类型的Assert:

Assert方法应用#

演示示例:
先建一个.net core类库项目,再建立一个xunit测试项目(参考最后综合示例)

Assert.True,Assert.False#

 

Copy

[Fact] [Trait("Category","New")] public void BeNewWhenCreated() { _output.WriteLine("第一个测试"); // Arrange var patient = new Patient(); // Act var result = patient.IsNew; // Assert Assert.True(result); }

字符串结果测试:Assert.Equal#

 

Copy

[Fact] public void HaveCorrectFullName() { //var patient = new Patient(); _patient.FirstName = "Nick"; _patient.LastName = "Carter"; var fullName = _patient.FullName; Assert.Equal("Nick Carter", fullName); //相等 Assert.StartsWith("Nick", fullName);//以开头 Assert.EndsWith("Carter", fullName);//以结尾 Assert.Contains("Carter", fullName);//包含 Assert.Contains("Car", fullName); Assert.NotEqual("CAR", fullName);//不相等 Assert.Matches(@"^[A-Z][a-z]*\s[A-Z][a-z]*", fullName);//正则表达式 }

数字结果测试#

 

Copy

[Fact] [Trait("Category", "New")] public void HaveDefaultBloodSugarWhenCreated() { var p = new Patient(); var bloodSugar = p.BloodSugar; Assert.Equal(4.9f, bloodSugar,5); //判断是否相等 Assert.InRange(bloodSugar, 3.9, 6.1);//判断是否在某一范围内 }

判断null,not null#

 

Copy

[Fact] public void HaveNoNameWhenCreated() { var p = new Patient(); Assert.Null(p.FirstName); Assert.NotNull(_patient); }

集合测试#

 

Copy

[Fact] public void HaveHadAColdBefore() { //Arrange var _patient = new Patient(); //Act var diseases = new List<string> { "感冒", "发烧", "水痘", "腹泻" }; _patient.History.Add("发烧"); _patient.History.Add("感冒"); _patient.History.Add("水痘"); _patient.History.Add("腹泻"); //Assert //判断集合是否含有或者不含有某个元素 Assert.Contains("感冒",_patient.History); Assert.DoesNotContain("心脏病", _patient.History); //判断p.History至少有一个元素,该元素以水开头 Assert.Contains(_patient.History, x => x.StartsWith("水")); //判断集合的长度 Assert.All(_patient.History, x => Assert.True(x.Length >= 2)); //判断集合是否相等,这里测试通过,说明是比较集合元素的值,而不是比较引用 Assert.Equal(diseases, _patient.History); }

测试对象#

 

Copy

/// <summary> /// 测试Object /// </summary> [Fact] public void BeAPerson() { var p = new Patient(); var p2 = new Patient(); Assert.IsNotType<Person>(p); //测试对象是否相等,注意这里为false Assert.IsType<Patient>(p); Assert.IsAssignableFrom<Person>(p);//判断对象是否继承自Person,true //判断是否为同一个实例 Assert.NotSame(p, p2); //Assert.Same(p, p2); }

判断是否发生异常#

 

Copy

/// <summary> /// 判断是否发生异常 /// </summary> [Fact] public void ThrowException() //注意不能使用ctrl+R,T快捷键,因为会中断测试,抛出异常 { var p = new Patient(); //判断是否返回指定类型的异常 var ex = Assert.Throws<InvalidOperationException>(()=> { p.NotAllowed(); }); //判断异常信息是否相等 Assert.Equal("not able to create", ex.Message); }

判断是否触发事件#

 

Copy

/// <summary> /// 判断是否触发事件 /// </summary> [Fact] public void RaizeSleepEvent() { var p = new Patient(); Assert.Raises<EventArgs>( handler=>p.PatientSlept+=handler, handler=>p.PatientSlept -= handler, () => p.Sleep()); }

判断属性改变是否触发事件#

 

Copy

/// <summary> /// 测试属性改变事件是否触发 /// </summary> [Fact] public void RaisePropertyChangedEvent() { var p = new Patient(); Assert.PropertyChanged(p, nameof(p.HeartBeatRate), () => p.IncreaseHeartBeatRate()); }

分组、忽略、log、共享上下文

测试分组#

使用trait特性,对测试进行分组:[Trait("Name","Value")] 可以作用于方法级和Class级别
相同的分组使用相同的特性。

 

Copy

[Fact] [Trait("Category","New")] public void BeNewWhenCreated() { _output.WriteLine("第一个测试"); // Arrange //var patient = new Patient(); // Act var result = _patient.IsNew; // Assert Assert.True(result); //Assert.False(result); }

测试分组搜索: 可以在测试资源管理器中按分组排列、搜索、运行测试

在dotnet cli中分组测试:

 

Copy

dotnew test --filter “Category=New” //运行单个分类测试 dotnew test --filter “Category=New|Category=Add”//运行多个分类测试 dotnet test --filter Category --logger:trx //输出测试日志

忽略测试#

使用特性:[Fact(Skip="不跑这个测试")],可以忽略测试,忽略测试图标为黄色警告

自定义测试输出内容#

使用ITestOutputHelper可以自定义在测试时的输出内容
dotnet test --filter Category --logger:trx会输出测试日志trx结尾的文件

 

Copy

public class PatientShould:IClassFixture<LongTimeFixture>,IDisposable { private readonly ITestOutputHelper _output; private readonly Patient _patient; private readonly LongTimeTask _task; public PatientShould(ITestOutputHelper output,LongTimeFixture fixture) { this._output = output; _patient = new Patient(); //_task = new LongTimeTask(); _task = fixture.Task; } [Fact] [Trait("Category","New")] public void BeNewWhenCreated() { _output.WriteLine("第一个测试"); // Arrange //var patient = new Patient(); // Act var result = _patient.IsNew; // Assert Assert.True(result); //Assert.False(result); } }

减少重复代码#

  1. 减少new对象,可以在构造函数中new,在方法中使用。
  2. 测试类实现IDispose接口,测试完释放资源,注意每个测试结束后都会调用Dispose方法。

共享上下文#

同一个测试类#

在执行一个方法时,需要很长事件,而在构造函数中new时,每个测试跑的时候都会new对象或者执行方法,这是导致测试很慢。解决方法:

  1. 创建一个类:
 

Copy

using Demo2; using System; namespace Demo2Test { public class LongTimeFixture : IDisposable { public LongTimeTask Task { get; } public LongTimeFixture() { } public void Dispose() { } } }

  1. 测试类实现IClassFixture<LongTimeFixture>接口,并在构造函数中获取方法
 

Copy

public class PatientShould:IClassFixture<LongTimeFixture>,IDisposable { private readonly ITestOutputHelper _output; private readonly Patient _patient; private readonly LongTimeTask _task; public PatientShould(ITestOutputHelper output,LongTimeFixture fixture) { this._output = output; _patient = new Patient(); //_task = new LongTimeTask(); _task = fixture.Task;//获取方法 } }

不同的测试类#

1.在上一个的继承上,先建立一个TaskCollection类,实现ICollectionFixture<LongTimeFixture>接口,注意不能有副作用,否则会影响结果

 

Copy

using Xunit; namespace Demo2Test { [CollectionDefinition("Lone Time Task Collection")] public class TaskCollection:ICollectionFixture<LongTimeFixture> { } }

  1. 使用,加上[Collection("Lone Time Task Collection")]
 

Copy

[Collection("Lone Time Task Collection")] public class PatientShould:IClassFixture<LongTimeFixture>,IDisposable { private readonly ITestOutputHelper _output; private readonly Patient _patient; private readonly LongTimeTask _task; public PatientShould(ITestOutputHelper output,LongTimeFixture fixture) { this._output = output; _patient = new Patient(); //_task = new LongTimeTask(); _task = fixture.Task;//获取方法 } }


数据共享

1. 使用[Theory],可以写有构造参数的测试方法,使用InlineData传递数据#

 

Copy

[Theory] [InlineData(1,2,3)] [InlineData(2,2,4)] [InlineData(3,3,6)] public void ShouldAddEquals(int operand1,int operand2,int expected) { //Arrange var sut = new Calculator(); //sut-system under test //Act var result = sut.Add(operand1, operand2); //Assert Assert.Equal(expected, result); }

2. 使用[MemberData]特性,可以在多个测试中使用#

  1. 先添加CalculatorTestData类:
 

Copy

using System.Collections.Generic; namespace DemoTest { public class CalculatorTestData { private static readonly List<object[]> Data = new List<object[]> { new object[]{ 1,2,3}, new object[]{ 1,3,4}, new object[]{ 2,4,6}, new object[]{ 0,1,1}, }; public static IEnumerable<object[]> TestData => Data; } }

  1. 使用MemberData
 

Copy

/// <summary> /// 数据共享-MemberData /// </summary> /// <param name="operand1"></param> /// <param name="operand2"></param> /// <param name="expected"></param> [Theory] [MemberData(nameof(CalculatorTestData.TestData),MemberType =typeof(CalculatorTestData))] public void ShouldAddEquals2(int operand1, int operand2, int expected) { //Arrange var sut = new Calculator(); //sut-system under test //Act var result = sut.Add(operand1, operand2); //Assert Assert.Equal(expected, result); }

3. 使用外部数据#

  1. 先创建一个类,准备数据,这里是读取的csv文件的数据
 

Copy

using System.Collections.Generic; using System.IO; using System.Linq; namespace DemoTest.Data { /// <summary> /// 读取文件并返回数据集合 /// </summary> public class CalculatorCsvData { public static IEnumerable<object[]> TestData { get { //把csv文件中的数据读出来,转换 string[] csvLines = File.ReadAllLines("Data\\TestData.csv"); var testCases = new List<object[]>(); foreach (var csvLine in csvLines) { IEnumerable<int> values = csvLine.Trim().Split(',').Select(int.Parse); object[] testCase = values.Cast<object>().ToArray(); testCases.Add(testCase); } return testCases; } } } }

  1. csv数据
 

Copy

1,2,3 1,3,4 2,4,6 0,1,1

  1. 使用
 

Copy

/// <summary> /// 数据共享-MemberData-外部数据 /// </summary> /// <param name="operand1"></param> /// <param name="operand2"></param> /// <param name="expected"></param> [Theory] [MemberData(nameof(CalculatorCsvData.TestData), MemberType = typeof(CalculatorCsvData))] public void ShouldAddEquals3(int operand1, int operand2, int expected) { //Arrange var sut = new Calculator(); //sut-system under test //Act var result = sut.Add(operand1, operand2); //Assert Assert.Equal(expected, result); }

4. 使用自定义特性,继承自DataAttribute#

  1. 自定义特性
 

Copy

using System.Collections.Generic; using System.Reflection; using Xunit.Sdk; namespace DemoTest.Data { public class CalculatorDataAttribute : DataAttribute { public override IEnumerable<object[]> GetData(MethodInfo testMethod) { yield return new object[] { 0, 100, 100 }; yield return new object[] { 1, 99, 100 }; yield return new object[] { 2, 98, 100 }; yield return new object[] { 3, 97, 100 }; } } }

  1. 使用
 

Copy

/// <summary> /// 数据共享-自定义特性继承自DataAttribute /// </summary> /// <param name="operand1"></param> /// <param name="operand2"></param> /// <param name="expected"></param> [Theory] [CalculatorDataAttribute] public void ShouldAddEquals4(int operand1, int operand2, int expected) { //Arrange var sut = new Calculator(); //sut-system under test //Act var result = sut.Add(operand1, operand2); //Assert Assert.Equal(expected, result); }

源码:https://gitee.com/Alexander360/LearnXUnit

 

 


http://chatgpt.dhexx.cn/article/lX4tRhdT.shtml

相关文章

Xunit入门

本节记录Xunit单元测试的入门知识&#xff0c;以2.1版本作为入门示例。 1、新建一个类库项目 2、在Nuget中搜索xunit&#xff0c;这里我们只选xUnit.net和xunit.runner.visualstudio包。 其中xUnit是框架&#xff0c;而xunit.runner.visualstudio是vs插件包&#xff0c;让我们可…

3. 使用xUnit进行单元测试

实现.NET Core时&#xff0c;xUnit可用于创建单元测试&#xff0c;.NET Core团队使用了该产品。xUnit是一个开源实现方案&#xff0c;创建NUnit 2.0的开发人员创建了它。现在&#xff0c;.NET Core命令行界面支持MSTest和xUnit。 提示&#xff1a; xUnit的文档可参阅https://…

详谈单元测试-xUnit

简介 xUnit.net 是针对 .NET 的免费&#xff0c;开源单元测试框架&#xff0c;可并行测试、数据驱动测试。测试项目需引用被测试项目&#xff0c;从而对其进行测试&#xff0c;测试项目同时需要引用 xUnit。测试编写完成后&#xff0c;用 Test Runner 来测试项目&#xff0c;T…

01 如何利用xUnit框架对测试用例进行维护-xUnit简介及基本使用方法(基于Junit4)

1、xUnit是什么 先看Wikipedia上的解释 xUnit是一系列测试框架的统称&#xff0c;最开始来源于一个叫做Smalltalk的SUnit框架&#xff0c;现在各种面向对象的语言&#xff0c;如Java、Python的鼻祖就是Smalltalk&#xff0c;后来这些语言都借助了Sunit框架的理念&#xff0c;有…

【IoT】物联网NB-IoT之移动oneNET平台硬件接入

主要实现开发者实际的终端设备在 OneNET 平台上的创建、连接和数据交互。在完成用户注册和产品创建后&#xff0c;即可根据相关所创建产品的协议类型选择相应的硬件接入的开发。 接入流程可参见下图&#xff1a; 1、LWM2M 协议 - NB-IoT 测试接入流程分为平台域和设备域&…

【安装工具】【ARM-DS-5】成功破解ARM DS-5 v5.26.0 + 配置高通MDM9026芯片的demo==》成功编译得到demo.bin文件

Note&#xff1a;Win7 64环境 安装破解ARM DS-5 v5.26.0 http://blog.csdn.net/qq_27295631/article/details/68582582 验证破解成功 安装MinGW http://blog.csdn.net/qq_27295631/article/details/68582582 设置系统属性-环境变量(bin/lib/inc) ARMBIN C:\Program Files…

把乌托邦变成细密画:华为如何思考家居IoT?

奥尔罕帕穆克凭借他的名作《我的名字叫红》一举跃入20世纪文学史&#xff0c;而这部广为人知的作品也让我们知道了一种绘画史上非常奇特的艺术形式&#xff1a;细密画。 这种流传自希腊罗马&#xff0c;盛行于波斯帝国的艺术样式&#xff0c;特点是要在书的扉页、边框&#xff…

MDM9607平台 Secure Boot调试记录

本博客只涉及技术问题,尊重原创,不涉及商用问题。 目录 一 概述... 4 二 原理及流程... 4 2.1 安全启动原理... 4 2.2 安全启动结构图:... 5 2.3 安全启动流程图... 5 三 证书简介... 6 四 签名镜像格式... 7 五 熔丝熔断... 7 5.1 熔断使能... 7 5.2 熔断过程..…

MDM9205简介

大家好&#xff0c;今天小白给大家介绍下高通新推出的下一代物联网专用调制解调器Qualcomm 9205&#xff0c;欢迎一起学习交流。 去年12月17日&#xff0c;高通宣布推出下一代物联网&#xff08;IoT&#xff09;专用调制解调器Qualcomm 9205。全新Qualcomm 9205 LTE调制解调器…

基于机智云gokit4.0(G)和MDM9206的 小型智能气象站

项目介绍 小型气象站广泛应用于气象、农业、环境检测和治理等领域。小型气象站对空气温湿度、光照、风速、风向、雨量、土壤湿度、蒸发量、大气压力等环境气象要素进行全天候现场检测。通过更换不同的传感器&#xff0c;该装置也可用于楼宇环境监控、安防、智慧农业等领域。 由…

MWC2018 机智云发布gokit4.0G全栈IoT开发套件,支持高通MDM9206

MWC2018 机智云发布gokit4.0G全栈IoT开发套件&#xff0c;支持高通MDM9206 NB-IoT/eMTC 继2月14日Qualcomm高通宣布&#xff0c;推出面向Qualcomm MDM9206LTE IoT全球多模调制解调器的全新LTEIoT软件开发包&#xff08;SDK&#xff09;&#xff0c;并已预集成机智云物联网云平…

【沙龙】基于MDM9206芯片的gokit4(G)的应用实操

导读 紧跟前沿通信技术、Get最新开发技能&#xff0c;高通Qualcomm&机智云&移远通信高级工程师手把手教开发&#xff1a;通过GoKit4(G)MDM9206快速接入机智云&#xff0c;4小时掌握高通MDM9206 C-IoT SDK实现不同IoT应用场景的方法&#xff0c;实现NB-IoT产品原型设计…

在线公开课】基于MDM9206的GoKit4(G)在线公开课

【在线公开课】基于MDM9206的GoKit4(G)在线公开课 基于Qualcomm C-IoT SDK的物联网开发以及GoKit4应用开发指导 MDM9206 LTE IoT调制解调器是一款专为支持全球多模功能而打造的解决方案&#xff0c;它可支持eMTC&#xff08;Cat M1&#xff09;、NB-IoT&#xff08;Cat NB-1&…

MDM9206简介

MDM9206简介 大家好&#xff0c;今天小白给大家介绍一下当前很火热的一款多模多频的高通的NB-IOT芯片&#xff0c;欢迎一起讨论学习。 1. MDM9206芯片的发展背景 接下来我们看看该款具有代表性的多模多频NB-IOT芯片是基于什么样的大背景下产生的&#xff1f; 从2G、3G如今的…

VMware虚拟机centOS7的终端命令:权限不够

我的VMware虚拟机centOS7在终端命令的时候老是跟我说权限不够&#xff1a; 然后我就去网上查查查&#xff01;有一个大佬的博客非常完美的解决了我的问题&#xff01; (5条消息) VM虚拟机CentOS7提升用户权限_JzjSunshine的博客-CSDN博客 现在&#xff1a; 但是我高兴了太早我执…

centos中常用命令

centos中常用命令 一:使用 CentOS 常用命令查看 cpu 如果觉得需要看的更加舒服 grep “model name” /proc/cpuinfo | cut -f2 -d: 二.使用 CentOS 常用命令查看 cpu 是 32 位还是 64 位 查看 CPU 位数(32 or 64) 三:使用 CentOS 常用命令查看当前 linux 的版本 more /etc/r…

CentOS 7关闭防火墙命令

1、命令行界面输入命令“systemctl status firewalld.service”并按下回车键。 2、然后在下方可以查看得到“active&#xff08;running&#xff09;”&#xff0c;此时说明防火墙已经被打开了。 3、在命令行中输入systemctl stop firewalld.service命令&#xff0c;进行关闭…

centos命令源码获取

源码链接 https://vault.centos.org/7.9.2009/os/Source/SPackages/ 查看本地命令版本 which ls rpm -qf /usr/bin/ls下载指定命令的rpm包 wget https://vault.centos.org/7.9.2009/os/Source/SPackages/coreutils-8.22-24.el7.src.rpm提取源码 rpm2cpio coreutils-8.22-24.…

centos下所有命令都不能用

问题描述&#xff1a; 解决办法&#xff1a; --- export PATH"/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin:/bin:/sbin" 总的来说&#xff0c;不说运行在linux上的一些应用&#xff0c;或者你之前部署过的产品&#xff0c;就是很多linux的基本命令&#xff0…