xUnit总结

什么是xUnit

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类库:Demo,添加一个Calculator类:
namespace Demo
{public class Calculator{public int Add(int x,int y){return x + y;}}
}
  1. 在同一解决方案,创建一个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);}
}
  1. 运行测试(任意一种方法):
    1. 通过vs自带的测试资源管理器,找到测试项目,选择运行;
    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

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

字符串结果测试:Assert.Equal

[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);//正则表达式
}

数字结果测试

[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

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

集合测试

[Fact]
public void HaveHadAColdBefore()
{//Arrangevar _patient = new Patient();//Actvar 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);}

测试对象

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

判断是否发生异常

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

判断是否触发事件

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

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

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

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

测试分组

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

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

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

在dotnet cli中分组测试:

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结尾的文件

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();// Actvar result = _patient.IsNew;// AssertAssert.True(result);//Assert.False(result);}
}

减少重复代码

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

共享上下文

同一个测试类

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

  1. 创建一个类:
using Demo2;
using System;namespace Demo2Test
{public class LongTimeFixture : IDisposable{public LongTimeTask Task { get; }public LongTimeFixture(){}public void Dispose(){}}
}
  1. 测试类实现IClassFixture<LongTimeFixture>接口,并在构造函数中获取方法
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>接口,注意不能有副作用,否则会影响结果

using Xunit;namespace Demo2Test
{[CollectionDefinition("Lone Time Task Collection")]public class TaskCollection:ICollectionFixture<LongTimeFixture>{}
}
  1. 使用,加上[Collection(“Lone Time Task Collection”)]
[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传递数据

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

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

  1. 先添加CalculatorTestData类:
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
/// <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)
{//Arrangevar sut = new Calculator(); //sut-system under test//Actvar result = sut.Add(operand1, operand2);//AssertAssert.Equal(expected, result);
}

3. 使用外部数据

  1. 先创建一个类,准备数据,这里是读取的csv文件的数据
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数据
1,2,3
1,3,4
2,4,6
0,1,1
  1. 使用
/// <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)
{//Arrangevar sut = new Calculator(); //sut-system under test//Actvar result = sut.Add(operand1, operand2);//AssertAssert.Equal(expected, result);
}

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

  1. 自定义特性
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. 使用
/// <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)
{//Arrangevar sut = new Calculator(); //sut-system under test//Actvar result = sut.Add(operand1, operand2);//AssertAssert.Equal(expected, result);
}

源码:见资源

单元测试-xUnit总结相关推荐

  1. 单元测试-xUnit

    单元测试-xUnit Assert常用方法 名称 描述 Eqalexpected.result) 断言结果与期望值相等.该方法的重载版本用于比较不同的类型和集合.该方法的另一种版本接受一个额外参数 该 ...

  2. C#/.net 单元测试xUnit、Mock、Moq

    C#/.net 单元测试xUnit.Mock.Moq 在做单元测试的时候,有时需要引用很多的外部对象,例如网络通信.记录日志等.单元测试无法控制这些外部的依赖对象,所以需要使用Stub和Mock来模拟 ...

  3. 详谈单元测试-xUnit

    简介 xUnit.net 是针对 .NET 的免费,开源单元测试框架,可并行测试.数据驱动测试.测试项目需引用被测试项目,从而对其进行测试,测试项目同时需要引用 xUnit.测试编写完成后,用 Tes ...

  4. .net、C#单元测试xUnit

    xUnit单元测试 测试的分类 单元测试:对某个类或者某个方法进行测试 集成测试:可使用Web资源.数据库数据进行测试 皮下测试:在Web中对controller下的节点测试 UI测试:对界面的功能进 ...

  5. 单元测试xUnit学习

    为什么要做自动化测试 可以频繁的进行测试 可以在任何时候进行测试,也可以按计划定时测试 比人工测试快 快速发现错误 可靠 测试代码与生产代码紧密结合 代码规范 自动化测试的分类 单元测试:可以测试某个 ...

  6. 单元测试中Assert详解-xUnit

    前一篇:详谈单元测试-xUnit 简介 Assert 是基于代码的返回值.对象的最终状态.事件是否发生等情况来评估测试的结果.Assert 的结果可能是 Pass 或者 Fail.如果所有的 Asse ...

  7. Xunit.net 单元测试学习——按顺序测试

    最近一直在提高编码的质量,为了保证质量,必须有一套趁手的兵器,Xunit.Net 就是如此霸气的利器. 怎么编写单元测试 怎么会有如此低级的疑问呢?因为从接触单元测试,到使用上,真的不是一个简单的心路 ...

  8. 使用 Visual Studio 2005 Team System 进行单元测试并生成用于 Unit Test Framework 的源代码...

    PS:微软专家写的一往篇VSTS2005测试功能说明,非常详细.适合初学者查看. 适用于: Microsoft Visual Studio 2005 Team System Beta 2 Team A ...

  9. .NET 测试框架 xUnit,MSTest, Specflow 使用经验汇总

    目录 前言 单元测试 单元测试就是对程序给定输入,判断输出是否满足预期 不是所有的代码都能编写单元测试 EF 单元测试 xUnit和MSTest Specflow 前言 xUnit和MSTest同属于 ...

最新文章

  1. ubuntu安装使用ffmpeg
  2. java.util.List学习笔记
  3. Catalan数(数论)
  4. 逸管家:别只共享单车,互联网时代还可以共享人才
  5. GPS时钟装置,北斗卫星授时,ntp校时服务器,网络时间服务器
  6. 俄罗斯方块 java_java代码实现俄罗斯方块
  7. 如何利用window下的Dos命令实现将多个txt合并成一个txt
  8. 两年后再次遇到的Oracle启动报错,ORA-01012:not logged
  9. RadioButton 只读
  10. 智能学习 | MATLAB实现Bee-CNN蜜蜂算法优化卷积神经网络图像分类预测
  11. html 复制链接功能,h5分享功能[通过复制网页链接分享]
  12. TensorFlow常用激活函数及其特点用法(6种)详解
  13. 苹果手机白屏_安卓用久了卡顿 苹果用久了闪退 究竟是因为啥?
  14. Ubuntu 18.04 Wine下安装微信并成功解决疑难杂症
  15. mysql连接数尖刺激增_mysql最大连接数max_connections
  16. Mac电脑 重设开机密码
  17. js进阶实例,数字时钟
  18. 华为hn–wx9x笔记本电脑_华为荣耀9怎样无线连接电脑。
  19. 感觉黑客帝国的时代不远了
  20. 转 C# GridView中DataFormatString属性的取值

热门文章

  1. 喝牛奶越喝越伤身的十大错招
  2. python 数组赋值后 内存区域是不是同一片_python – numpy数组赋值问题
  3. python利用setsockopt获得端口重用
  4. Create React App来搭建react项目
  5. http域名免费升级https
  6. 2021年在线教育行业现状与前景
  7. 卡萨帝洗衣机这场技术战重新定义了滚筒
  8. 全球及中国血管介入医疗器械行业市场营销现状及未来趋势预测分析报告2022-2028年
  9. 顶级的18 款开源低代码开发平台
  10. Just for fun ----Linux创造的那些传奇