• xUnit总结
    • #">什么是xUnit#
    • #">自动化测试的优点#
    • #">自动化测试的分类#
    • #">是测试行为还是测试私有方法#
    • #">xUnit.Net特点:#
    • #">xUnit支持的平台:#
    • #">测试工具:#
    • #">简单的例子#
    • #">测试的三个阶段:AAA#
  • Assert
    • #">Assert方法应用#
      • #">Assert.True,Assert.False#
      • #">字符串结果测试:Assert.Equal#
      • #">数字结果测试#
      • #">判断null,not null#
      • #">集合测试#
      • #">测试对象#
      • #">判断是否发生异常#
      • #">判断是否触发事件#
      • #">判断属性改变是否触发事件#
  • 分组、忽略、log、共享上下文
    • #">测试分组#
    • #">忽略测试#
    • #">自定义测试输出内容#
    • #">减少重复代码#
    • #">共享上下文#
      • #">同一个测试类#
      • #">不同的测试类#
  • 数据共享
    • #">1. 使用[Theory],可以写有构造参数的测试方法,使用InlineData传递数据#
    • #">2. 使用[MemberData]特性,可以在多个测试中使用#
    • #">3. 使用外部数据#
    • #">4. 使用自定义特性,继承自DataAttribute#

    xUnit总结

    什么是xUnit#

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

    自动化测试的优点#

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

      自动化测试的分类#

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

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

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

      xUnit.Net特点:#

    • 支持多平台/运行时

    • 并行测试
    • 数据驱动测试
    • 可扩展

      xUnit支持的平台:#

      .Net Framework
      .Net Core
      .Net Standard
      UWP
      Xamarin
      官网:
      https://xunit.net

      测试工具:#

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

    简单的例子#

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

      1. namespace Demo
      2. {
      3. public class Calculator
      4. {
      5. public int Add(int x,int y)
      6. {
      7. return x + y;
      8. }
      9. }
      10. }
    2. 在同一解决方案,创建一个xUnit测试项目:DemoTest,针对项目测试,一般是项目名+Test命名测试项目。创建一个类:CalculatorTests:

      1. public class CalculatorTests
      2. {
      3. [Fact]
      4. public void ShouldAddEquals5() //注意命名规范
      5. {
      6. //Arrange
      7. var sut = new Calculator(); //sut-system under test,通用命名
      8. //Act
      9. var result = sut.Add(3, 2);
      10. //Assert
      11. Assert.Equal(5, result);
      12. }
      13. }
    3. 运行测试(任意一种方法):

      1. 通过vs自带的测试资源管理器,找到测试项目,选择运行;

    0.0016-单元测试-xUnit总结 - 图3

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

    0.0016-单元测试-xUnit总结 - 图4

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

      测试的三个阶段:AAA#

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

    Assert

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

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

    0.0016-单元测试-xUnit总结 - 图7

    Assert方法应用#

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

    Assert.True,Assert.False#

    1. [Fact]
    2. [Trait("Category","New")]
    3. public void BeNewWhenCreated()
    4. {
    5. _output.WriteLine("第一个测试");
    6. // Arrange
    7. var patient = new Patient();
    8. // Act
    9. var result = patient.IsNew;
    10. // Assert
    11. Assert.True(result);
    12. }

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

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

    数字结果测试#

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

    判断null,not null#

    1. [Fact]
    2. public void HaveNoNameWhenCreated()
    3. {
    4. var p = new Patient();
    5. Assert.Null(p.FirstName);
    6. Assert.NotNull(_patient);
    7. }

    集合测试#

    1. [Fact]
    2. public void HaveHadAColdBefore()
    3. {
    4. //Arrange
    5. var _patient = new Patient();
    6. //Act
    7. var diseases = new List<string>
    8. {
    9. "感冒",
    10. "发烧",
    11. "水痘",
    12. "腹泻"
    13. };
    14. _patient.History.Add("发烧");
    15. _patient.History.Add("感冒");
    16. _patient.History.Add("水痘");
    17. _patient.History.Add("腹泻");
    18. //Assert
    19. //判断集合是否含有或者不含有某个元素
    20. Assert.Contains("感冒",_patient.History);
    21. Assert.DoesNotContain("心脏病", _patient.History);
    22. //判断p.History至少有一个元素,该元素以水开头
    23. Assert.Contains(_patient.History, x => x.StartsWith("水"));
    24. //判断集合的长度
    25. Assert.All(_patient.History, x => Assert.True(x.Length >= 2));
    26. //判断集合是否相等,这里测试通过,说明是比较集合元素的值,而不是比较引用
    27. Assert.Equal(diseases, _patient.History);
    28. }

    测试对象#

    1. /// <summary>
    2. /// 测试Object
    3. /// </summary>
    4. [Fact]
    5. public void BeAPerson()
    6. {
    7. var p = new Patient();
    8. var p2 = new Patient();
    9. Assert.IsNotType<Person>(p); //测试对象是否相等,注意这里为false
    10. Assert.IsType<Patient>(p);
    11. Assert.IsAssignableFrom<Person>(p);//判断对象是否继承自Person,true
    12. //判断是否为同一个实例
    13. Assert.NotSame(p, p2);
    14. //Assert.Same(p, p2);
    15. }

    判断是否发生异常#

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

    判断是否触发事件#

    1. /// <summary>
    2. /// 判断是否触发事件
    3. /// </summary>
    4. [Fact]
    5. public void RaizeSleepEvent()
    6. {
    7. var p = new Patient();
    8. Assert.Raises<EventArgs>(
    9. handler=>p.PatientSlept+=handler,
    10. handler=>p.PatientSlept -= handler,
    11. () => p.Sleep());
    12. }

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

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

    测试分组#

    使用trait特性,对测试进行分组:[Trait(“Name”,”Value”)] 可以作用于方法级和Class级别
    相同的分组使用相同的特性。
    测试分组搜索: 可以在测试资源管理器中按分组排列、搜索、运行测试
    0.0016-单元测试-xUnit总结 - 图8
    在dotnet cli中分组测试:

    忽略测试#

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

    自定义测试输出内容#

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

    减少重复代码#

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

      共享上下文#

      同一个测试类#

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

    3. 创建一个类:

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

      不同的测试类#

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

    5. 使用,加上[Collection(“Lone Time Task Collection”)]


    数据共享

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

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

    1. 先添加CalculatorTestData类:
    2. 使用MemberData

      3. 使用外部数据#

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

    4. csv数据
    5. 使用

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

    6. 自定义特性

    7. 使用

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