第一部分: http://www.cnblogs.com/cgzl/p/8283610.html

下面有一点点内容是重叠的....

String Assert

测试string是否相等

  1. [Fact]
  2. public void CalculateFullName()
  3. {
  4. var p = new Patient
  5. {
  6. FirstName = "Nick",
  7. LastName = "Carter"
  8. };
  9. Assert.Equal("Nick Carter", p.FullName);
  10. }

然后你需要Build一下,这样VS Test Explorer才能发现新的test。

运行测试,结果Pass:

同样改一下Patient类(别忘了Build一下),让结果失败:

从失败信息可以看到期待值和实际值。

StartsWith, EndsWith

  1. [Fact]
  2. public void CalculateFullNameStartsWithFirstName()
  3. {
  4. var p = new Patient
  5. {
  6. FirstName = "Nick",
  7. LastName = "Carter"
  8. };
  9. Assert.StartsWith("Nick", p.FullName);
  10. }
  11.  
  12. [Fact]
  13. public void CalculateFullNameEndsWithFirstName()
  14. {
  15. var p = new Patient
  16. {
  17. FirstName = "Nick",
  18. LastName = "Carter"
  19. };
  20. Assert.EndsWith("Carter", p.FullName);e);
  21. }

Build,然后Run Test,结果Pass:

忽略大小写 ignoreCase

string默认的Assert是区分大小写的,这样就会失败:

可以为这些方法添加一个参数ignoreCase设置为true,就会忽略大小写:

包含子字符串 Contains

  1. [Fact]
  2. public void CalculateFullNameSubstring()
  3. {
  4. var p = new Patient
  5. {
  6. FirstName = "Nick",
  7. LastName = "Carter"
  8. };
  9. Assert.Contains("ck Ca", p.FullName);
  10. }

Build,测试结果Pass。

正则表达式,Matches

测试一下First name和Last name的首字母是不是大写的:

  1. [Fact]
  2. public void CalculcateFullNameWithTitleCase()
  3. {
  4. var p = new Patient
  5. {
  6. FirstName = "Nick",
  7. LastName = "Carter"
  8. };
  9. Assert.Matches("[A-Z]{1}{a-z}+ [A-Z]{1}[a-z]+", p.FullName);
  10. }

Build,测试通过。

数值 Assert

首先为Patient类添加一个property: BloodSugar。

  1. public class Patient
  2. {
  3. public Patient()
  4. {
  5. IsNew = true;
  6. _bloodSugar = 5.0f;
  7. }
  8.  
  9. private float _bloodSugar;
  10. public float BloodSugar
  11. {
  12. get { return _bloodSugar; }
  13. set { _bloodSugar = value; }
  14. }
  15. ...

Equal:

  1. [Fact]
  2. public void BloodSugarStartWithDefaultValue()
  3. {
  4. var p = new Patient();
  5. Assert.Equal(5.0, p.BloodSugar);
  6. }

Build,测试通过。

范围, InRange:

首先为Patient类添加一个方法,病人吃饭之后血糖升高:

  1. public void HaveDinner()
  2. {
  3. var random = new Random();
  4. _bloodSugar += (float)random.Next(1, 1000) / 100; // 应该是1000
  5. }

添加test:

  1. [Fact]
  2. public void BloodSugarIncreaseAfterDinner()
  3. {
  4. var p = new Patient();
  5. p.HaveDinner();
  6. // Assert.InRange<float>(p.BloodSugar, 5, 6);
  7. Assert.InRange(p.BloodSugar, 5, 6);
  8. }

Build,Run Test,结果Fail:

可以看到期待的Range和实际的值,这样很好。如果你使用Assert.True(xx >= 5 && xx <= 6)的话,错误信息只能显示True或者False。

因为HaveDinner方法里,表达式的分母应该是1000,修改后,Build,Run,测试Pass。

浮点型数值的Assert

在被测项目添加这两个类:

  1. namespace Hospital
  2. {
  3. public abstract class Worker
  4. {
  5. public string Name { get; set; }
  6.  
  7. public abstract double TotalReward { get; }
  8. public abstract double Hours { get; }
  9. public double Salary => TotalReward / Hours;
  10. }
  11.  
  12. public class Plumber : Worker
  13. {
  14. public override double TotalReward => ;
  15. public override double Hours => ;
  16. }
  17. }

然后针对Plumber建立一个测试类 PlumberShould.cs, 并建立第一个test:

  1. namespace Hospital.Tests
  2. {
  3. public class PlumberShould
  4. {
  5. [Fact]
  6. public void HaveCorrectSalary()
  7. {
  8. var plumber = new Plumber();
  9. Assert.Equal(66.666, plumber.Salary);
  10. }
  11. }
  12. }

Build项目, 然后再Test Explorer里面选择按Class分类显示Tests:

Run Selected Test, 结果会失败:

这是一个精度的问题.

在Assert.Equal方法, 可以添加一个precision参数, 设置精度为3:

  1. [Fact]
  2. public void HaveCorrectSalary()
  3. {
  4. var plumber = new Plumber();
  5. Assert.Equal(66.666, plumber.Salary, precision: 3);
  6. }

Build, Run Test:

因为有四舍五入的问题, 所以test仍然fail了.

所以还需要改一下:

  1. [Fact]
  2. public void HaveCorrectSalary()
  3. {
  4. var plumber = new Plumber();
  5. Assert.Equal(66.66, plumber.Salary, precision: );
  6. }

这次会pass的:

Assert Null值

  1. [Fact]
  2. public void NotHaveNameByDefault()
  3. {
  4. var plumber = new Plumber();
  5. Assert.Null(plumber.Name);
  6. }
  7.  
  8. [Fact]
  9. public void HaveNameValue()
  10. {
  11. var plumber = new Plumber
  12. {
  13. Name = "Brian"
  14. };
  15. Assert.NotNull(plumber.Name);
  16. }

有两个方法, Assert.Null 和 Assert.NotNull, 直接传入期待即可.

测试会Pass的.

集合 Collection Assert

修改一下被测试类, 添加一个集合属性, 并赋值:

  1. namespace Hospital
  2. {
  3. public abstract class Worker
  4. {
  5. public string Name { get; set; }
  6.  
  7. public abstract double TotalReward { get; }
  8. public abstract double Hours { get; }
  9. public double Salary => TotalReward / Hours;
  10.  
  11. public List<string> Tools { get; set; }
  12. }
  13.  
  14. public class Plumber : Worker
  15. {
  16. public Plumber()
  17. {
  18. Tools = new List<string>()
  19. {
  20. "螺丝刀",
  21. "扳子",
  22. "钳子"
  23. };
  24. }
  25.  
  26. public override double TotalReward => ;
  27. public override double Hours => ;
  28. }
  29. }

测试是否包含某个元素, Assert.Contains():

  1. [Fact]
  2. public void HaveScrewdriver()
  3. {
  4. var plumber = new Plumber();
  5. Assert.Contains("螺丝刀", plumber.Tools);
  6. }

Build, Run Test, 结果Pass.

修改一下名字, 让其Fail:

这个失败信息还是很详细的.

相应的还有一个Assert.DoesNotContain()方法, 测试集合是否不包含某个元素.

  1. [Fact]
  2. public void NotHaveKeyboard()
  3. {
  4. var plumber = new Plumber();
  5. Assert.DoesNotContain("键盘", plumber.Tools);
  6. }

这个test也会pass.

Predicate:

测试一下集合中是否包含符合某个条件的元素:

  1. [Fact]
  2. public void HaveAtLeastOneScrewdriver()
  3. {
  4. var plumber = new Plumber();
  5. Assert.Contains(plumber.Tools, t => t.Contains("螺丝刀"));
  6. }

使用的是Assert.Contains的一个overload方法, 它的第一个参数是集合, 第二个参数是Predicate.

Build, Run Test, 会Pass的.

比较集合相等:

添加Test:

  1. [Fact]
  2. public void HaveAllTools()
  3. {
  4. var plumber = new Plumber();
  5. var expectedTools = new []
  6. {
  7. "螺丝刀",
  8. "扳子",
  9. "钳子"
  10. };
  11. Assert.Equal(expectedTools, plumber.Tools);
  12. }

注意, Plumber的tools类型是List, 这里的expectedTools类型是array.

这个test 仍然会Pass.

如果修改一个元素, 那么测试会Fail, 信息如下:

Assert针对集合的每个元素:

如果想对集合的每个元素进行Assert, 当然可以通过循环来Assert了, 但是更好的写法是调用Assert.All()方法:

  1. [Fact]
  2. public void HaveNoEmptyDefaultTools()
  3. {
  4. var plumber = new Plumber();
  5. Assert.All(plumber.Tools, t => Assert.False(string.IsNullOrEmpty(t)));
  6. }

这个测试会Pass.

如果在被测试类的Tools属性添加一个空字符串, 那么失败信息会是:

这里写到, 4个元素里面有1个没有pass.

针对Object类型的Assert

首先再添加一个Programmer类:

  1. public class Programmer : Worker
  2. {
  3. public override double TotalReward => ;
  4. public override double Hours => 3.5;
  5. }

然后建立一个WorkerFactory:

  1. namespace Hospital
  2. {
  3. public class WorkerFactory
  4. {
  5. public Worker Create(string name, bool isProgrammer = false)
  6. {
  7. if (isProgrammer)
  8. {
  9. return new Programmer { Name = name };
  10. }
  11. return new Plumber { Name = name };
  12. }
  13. }
  14. }

判断是否是某个类型 Assert.IsType<Type>(xx):
建立一个测试类 WorkerShould.cs和一个test:

  1. namespace Hospital.Tests
  2. {
  3. public class WorkerShould
  4. {
  5. [Fact]
  6. public void CreatePlumberByDefault()
  7. {
  8. var factory = new WorkerFactory();
  9. Worker worker = factory.Create("Nick");
  10. Assert.IsType<Plumber>(worker);
  11. }
  12. }
  13. }

Build, Run Test: 结果Pass.

相应的, 还有一个Assert.IsNotType<Type>(xx)方法.

利用Assert.IsType<Type>(xx)的返回值, 它会返回Type(xx的)的这个实例, 添加个一test:

  1. [Fact]
  2. public void CreateProgrammerAndCastReturnedType()
  3. {
  4. var factory = new WorkerFactory();
  5. Worker worker = factory.Create("Nick", isProgrammer: true);
  6. Programmer programmer = Assert.IsType<Programmer>(worker);
  7. Assert.Equal("Nick", programmer.Name);
  8. }

Build, Run Tests: 结果Pass.

Assert针对父类:

写这样一个test, 创建的是一个promgrammer, Assert的类型是它的父类Worker:

  1. [Fact]
  2. public void CreateProgrammer_AssertAssignableTypes()
  3. {
  4. var factory = new WorkerFactory();
  5. Worker worker = factory.Create("Nick", isProgrammer: true);
  6. Assert.IsType<Worker>(worker);
  7. }

这个会Fail:

这时就应该使用这个方法, Assert.IsAssignableFrom<祖先类>(xx):

  1. [Fact]
  2. public void CreateProgrammer_AssertAssignableTypes()
  3. {
  4. var factory = new WorkerFactory();
  5. Worker worker = factory.Create("Nick", isProgrammer: true);
  6. Assert.IsAssignableFrom<Worker>(worker);
  7. }

Build, Run Tests: Pass.

Assert针对对象的实例

判断两个引用是否指向不同的实例 Assert.NotSame(a, b):

  1. [Fact]
  2. public void CreateSeperateInstances()
  3. {
  4. var factory = new WorkerFactory();
  5. var p1 = factory.Create("Nick");
  6. var p2 = factory.Create("Nick");
  7. Assert.NotSame(p1, p2);
  8. }

由工厂创建的两个对象是不同的实例, 所以这个test会Pass.

相应的还有个Assert.Same(a, b) 方法.

Assert 异常

为WorkFactory先添加一个异常处理:

  1. namespace Hospital
  2. {
  3. public class WorkerFactory
  4. {
  5. public Worker Create(string name, bool isProgrammer = false)
  6. {
  7. if (name == null)
  8. {
  9. throw new ArgumentNullException(nameof(name));
  10. }
  11. if (isProgrammer)
  12. {
  13. return new Programmer { Name = name };
  14. }
  15. return new Plumber { Name = name };
  16. }
  17. }
  18. }

如果在test执行代码时抛出异常的话, 那么test会直接fail掉.

所以应该使用Assert.Throws<ArgumentNullException>(...)方法来Assert是否抛出了特定类型的异常.

添加一个test:

  1. [Fact]
  2. public void NotAllowNullName()
  3. {
  4. var factory = new WorkerFactory();
    // var p = factory.Create(null); // 这个会失败
  5. Assert.Throws<ArgumentNullException>(() => factory.Create(null));
  6. }

注意不要直接运行会抛出异常的代码. 应该在Assert.Throws<ET>()的方法里添加lambda表达式来调用方法.

这样的话就会pass.

如果被测试代码没有抛出异常的话, 那么test会fail的. 把抛异常代码注释掉之后再Run:

更具体的, 还可以指定参数的名称:

  1. [Fact]
  2. public void NotAllowNullName()
  3. {
  4. var factory = new WorkerFactory();
  5. // Assert.Throws<ArgumentNullException>(() => factory.Create(null));
  6. Assert.Throws<ArgumentNullException>("name", () => factory.Create(null));
  7. }

这里就是说异常里应该有一个叫name的参数.

Run: Pass.

如果把"name"改成"isProgrammer", 那么这个test会fail:

利用Assert.Throws<ET>()的返回结果, 其返回结果就是这个抛出的异常实例.

  1. [Fact]
  2. public void NotAllowNullNameAndUseReturnedException()
  3. {
  4. var factory = new WorkerFactory();
  5. ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => factory.Create(null));
  6. Assert.Equal("name", ex.ParamName);
  7. }

Assert Events 是否发生(Raised)

回到之前的Patient类, 添加如下代码:

  1. public void Sleep()
  2. {
  3. OnPatientSlept();
  4. }
  5.  
  6. public event EventHandler<EventArgs> PatientSlept;
  7.  
  8. protected virtual void OnPatientSlept()
  9. {
  10. PatientSlept?.Invoke(this, EventArgs.Empty);
  11. }

然后回到PatientShould.cs添加test:

  1. [Fact]
  2. public void RaiseSleptEvent()
  3. {
  4. var p = new Patient();
  5. Assert.Raises<EventArgs>(
  6. handler => p.PatientSlept += handler,
  7. handler => p.PatientSlept -= handler,
  8. () => p.Sleep());
  9. }

Assert.Raises<T>()第一个参数是附加handler的Action, 第二个参数是分离handler的Action, 第三个Action是触发event的代码.

Build, Run Test: Pass.

如果注释掉Patient类里Sleep()方法内部那行代码, 那么test会fail:

针对INotifyPropertyChanged的特殊Assert:

修改Patient代码:

  1. namespace Hospital
  2. {
  3. public class Patient: INotifyPropertyChanged
  4. {
  5. public Patient()
  6. {
  7. IsNew = true;
  8. _bloodSugar = 5.0f;
  9. }
  10.  
  11. public string FirstName { get; set; }
  12. public string LastName { get; set; }
  13. public string FullName => $"{FirstName} {LastName}";
  14. public int HeartBeatRate { get; set; }
  15. public bool IsNew { get; set; }
  16.  
  17. private float _bloodSugar;
  18. public float BloodSugar
  19. {
  20. get => _bloodSugar;
  21. set => _bloodSugar = value;
  22. }
  23.  
  24. public void HaveDinner()
  25. {
  26. var random = new Random();
  27. _bloodSugar += (float)random.Next(, ) / ;
  28. OnPropertyChanged(nameof(BloodSugar));
  29. }
  30.  
  31. public void IncreaseHeartBeatRate()
  32. {
  33. HeartBeatRate = CalculateHeartBeatRate() + ;
  34. }
  35.  
  36. private int CalculateHeartBeatRate()
  37. {
  38. var random = new Random();
  39. return random.Next(, );
  40. }
  41.  
  42. public void Sleep()
  43. {
  44. OnPatientSlept();
  45. }
  46.  
  47. public event EventHandler<EventArgs> PatientSlept;
  48.  
  49. protected virtual void OnPatientSlept()
  50. {
  51. PatientSlept?.Invoke(this, EventArgs.Empty);
  52. }
  53.  
  54. public event PropertyChangedEventHandler PropertyChanged;
  55.  
  56. protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  57. {
  58. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  59. }
  60. }
  61. }

添加一个Test:

  1. [Fact]
  2. public void RaisePropertyChangedEvent()
  3. {
  4. var p = new Patient();
  5. Assert.PropertyChanged(p, "BloodSugar", () => p.HaveDinner());
  6. }

针对INotifyPropertyChanged, 可以使用Assert.PropertyChanged(..) 这个专用的方法来断定PropertyChanged的Event是否被触发了.

Build, Run Tests: Pass.

到目前为止, 介绍的都是入门级的内容.

接下来要介绍的是稍微进阶一点的内容了.

出处:http://www.cnblogs.com/cgzl/p/8287588.html

使用xUnit为.net core程序进行单元测试(2)的更多相关文章

  1. 使用xUnit为.net core程序进行单元测试(上)

    一. 导读 为什么要编写自动化测试程序(Automated Tests)? 可以频繁的进行测试 可以在任何时间进行测试,也可以按计划定时进行,例如:可以在半夜进行自动测试. 肯定比人工测试要快. 可以 ...

  2. 使用xUnit为.net core程序进行单元测试(1)

    导读 为什么要编写自动化测试程序(Automated Tests)? 可以频繁的进行测试 可以在任何时间进行测试,也可以按计划定时进行,例如:可以在半夜进行自动测试. 肯定比人工测试要快. 可以更快速 ...

  3. 使用xUnit为.net core程序进行单元测试(中)

    第一部分: http://www.cnblogs.com/cgzl/p/8283610.html 下面有一点点内容是重叠的.... String Assert 测试string是否相等: [Fact] ...

  4. 使用xUnit为.net core程序进行单元测试(3)

    第1部分: http://www.cnblogs.com/cgzl/p/8283610.html 第2部分: http://www.cnblogs.com/cgzl/p/8287588.html 请使 ...

  5. 使用xUnit为.net core程序进行单元测试(4)

    第1部分: http://www.cnblogs.com/cgzl/p/8283610.html 第2部分: http://www.cnblogs.com/cgzl/p/8287588.html 第3 ...

  6. 使用xUnit为.net core程序进行单元测试 -- Assert

    第一部分: http://www.cnblogs.com/cgzl/p/8283610.html Assert Assert做什么?Assert基于代码的返回值.对象的最终状态.事件是否发生等情况来评 ...

  7. 使用xUnit为.net core程序进行单元测试

      第1部分: http://www.cnblogs.com/cgzl/p/8283610.html 第2部分: http://www.cnblogs.com/cgzl/p/8287588.html ...

  8. 好代码是管出来的——.Net Core中的单元测试与代码覆盖率

    测试对于软件来说,是保证其质量的一个重要过程,而测试又分为很多种,单元测试.集成测试.系统测试.压力测试等等,不同的测试的测试粒度和测试目标也不同,如单元测试关注每一行代码,集成测试关注的是多个模块是 ...

  9. .NET Core: 在.NET Core中进行单元测试

    单元测试能够帮助开发人员确保所开发的模块.类以及类中的方法等的正确性,在项目开发过程中,及时进行单元测试能够避免不必要的BUG以及提高测试效率. 在本文中,我们会分别来学习如何使用MSTest.xUn ...

随机推荐

  1. MFC中Doc类获取View类的方法(SDI)

    从view类中获取Doc的方法如下: CYourDoc* pDoc = GetDocument(); 这个函数已经写好,所以无需自己添加,使用时直接利用pDoc即可. 若反过来,从Doc中获取View ...

  2. 对java沙箱机制的一点了解

    1.   引入 我们都知道,程序员编写一个Java程序,默认的情况下可以访问该机器的任意资源,比如读取,删除一些文件或者网络操作等.当你把程序部署到正式的服务器上,系统管理员要为服务器的安全承担责任, ...

  3. 文件读写网络IO简单了解,同步IO和异步IO

    在Linux中,对文件的读写其实就是IO. 与IO有关的名词:同步,异步,阻塞,非阻塞,甚至是同步阻塞,同步非阻塞,异步阻塞,异步非阻塞.别急,下面有举例IO分为两大种,同步和异步 同步IO:阻塞IO ...

  4. bzoj2721 / P1445 [Violet]樱花

    P1445 [Violet]樱花 显然$x,y>n$ 那么我们可以设$a=n!,y=a+t(t>0)$ 再对原式通分一下$a(a+t)+ax=x(a+t)$ $a^{2}+at+ax=ax ...

  5. SqlServer2012数据导入

    1.选择数据库,右击[任务]-->[导入数据]: 2.选择对应的数据源,和数据文件,下一步: 3.填写服务器地址,和数据库的登录信息,选择数据库名称: 4.复制一个或多个表或试图的数据: 5.将 ...

  6. 20145204 《Java程序设计》第9周学习总结

    20145204 <Java程序设计>第9周学习总结 教材学习内容总结 JDBC Java语言访问数据库的一种规范,是一套API.JDBC (Java Database Connectiv ...

  7. 20145325张梓靖 《Java程序设计》第5周学习总结

    20145325张梓靖 <Java程序设计>第5周学习总结 教材学习内容总结 try catch Java中所有错误都会被打包为对象.如果某个方法声明会抛出Throwable或子类实例,只 ...

  8. 关于JS和JSON

    讲得不准确! 看网课,JS也算是面向对象的一门语言,不过其是解释性的脚本语言. JSON是把用JS的表示法将数据包装起来进行传递用的. JS语法是松散型的,没有int String这些像JAVA里的类 ...

  9. HDU 4745 Two Rabbits(最长回文子序列)

    http://acm.hdu.edu.cn/showproblem.php?pid=4745 题意: 有一个环,现在有两只兔子各从一个点开始起跳,一个沿顺时针,另一个沿逆时针,只能在一圈之内跳,并且每 ...

  10. java学习之浅谈多线程1

    创建任务和线程 任务就是对象,为了创建任务,必须首先为任务定义一个类.任务类必须实现Runnable接口.Runnable接口非常简单,它只有一个run方法.需要实现这个方法来告诉系统线程将如何运行. ...