做第一个测试
对测试项目的文件名进行一些重构,编写以下代码,并进行Build:
从Test Explorer我们可以看到一个待测试的项目。
在这里,我们可以对测试项目进行分组和排序,如图:
想要运行所有的测试,就点击上面的Run All按钮。如果像运行单个测试,那么右击选择Run Selected Tests:
运行后,可以看到结果,Passed:
我们同样可以通过命令行来进行测试:
进入到Tests目录,执行 dotnet test命令,所有的测试都会被发现,然后被执行:
因为我们并没有在测试方法中写任何的Assert,所以测试肯定是通过的,但这个测试也是个无效的测试。
Assert
Assert做什么?Assert基于代码的返回值、对象的最终状态、事件是否发生等情况来评估测试的结果。Assert的结果可能是Pass或者Fail。如果所有的asserts都pass了,那么整个测试就pass了;如果有任何assert fail了,那么测试就fail了。
xUnit提供了以下类型的Assert:
●boolean:True/False
●String:相等/不等,是否为空,以..开始/结束,是否包含子字符串,匹配正则表达式
●数值型:相等/不等,是否在某个范围内,浮点的精度
●Collection:内容是否相等,是否包含某个元素,是否包含满足某种条件(predicate)的元素,是否所有的元素都满足某个assert
●Raised events:Custom events,Framework events(例如:PropertyChanged)
●Object Type:是否是某种类型,是否某种类型或继承与某种类型
一个test里应该有多少个asserts?
一种建议的做法是,每个test方法里面只有一个assert。
而还有一种建议就是,每个test里面可以有多个asserts,只要这些asserts都是针对同一个行为就行。
第一个Assert
目标类:
public class Patient { public Patient() { IsNew = true; } public string FirstName { get; set; } public string LastName { get; set; } public string FullName => $"{FirstName} {LastName}"; public int HeartBeatRate { get; set; } public bool IsNew { get; set; } public void IncreaseHeartBeatRate() { HeartBeatRate = CalculateHeartBeatRate() + 2; } private int CalculateHeartBeatRate() { var random = new Random(); return random.Next(1, 100); } } |
测试类:
public class PatientShould { [Fact] public void HaveHeartBeatWhenNew() { var patient = new Patient(); Assert.True(patient.IsNew); } } |
运行测试:
结果符合预期,测试通过。
改为Assert.False()的话:
测试Fail。
String Assert
测试string是否相等:
[Fact] public void CalculateFullName() { var p = new Patient { FirstName = "Nick", LastName = "Carter" }; Assert.Equal("Nick Carter", p.FullName); } |
然后你需要Build一下,这样VS Test Explorer才能发现新的test。
运行测试,结果Pass:
同样改一下Patient类(别忘了Build一下),让结果失败:
从失败信息可以看到期待值和实际值。
StartsWith, EndsWith
[Fact] public void CalculateFullNameStartsWithFirstName() { var p = new Patient { FirstName = "Nick", LastName = "Carter" }; Assert.StartsWith("Nick", p.FullName); } [Fact] public void CalculateFullNameEndsWithFirstName() { var p = new Patient { FirstName = "Nick", LastName = "Carter" }; Assert.EndsWith("Carter", p.FullName);e); } |
Build,然后Run Test,结果Pass:
忽略大小写 ignoreCase:
string默认的Assert是区分大小写的,这样就会失败:
可以为这些方法添加一个参数ignoreCase设置为true,就会忽略大小写:
包含子字符串 Contains
[Fact] public void CalculateFullNameSubstring() { var p = new Patient { FirstName = "Nick", LastName = "Carter" }; Assert.Contains("ck Ca", p.FullName); } |
Build,测试结果Pass。
正则表达式,Matches
测试一下First name和Last name的首字母是不是大写的:
[Fact] public void CalculcateFullNameWithTitleCase() { var p = new Patient { FirstName = "Nick", LastName = "Carter" }; Assert.Matches("[A-Z]{1}{a-z}+ [A-Z]{1}[a-z]+", p.FullName); } |
Build,测试通过。
数值 Assert
首先为Patient类添加一个property: BloodSugar。
public class Patient { public Patient() { IsNew = true; _bloodSugar = 5.0f; } private float _bloodSugar; public float BloodSugar { get { return _bloodSugar; } set { _bloodSugar = value; } } ... |
Equal:
[Fact] public void BloodSugarStartWithDefaultValue() { var p = new Patient(); Assert.Equal(5.0, p.BloodSugar); } |
Build,测试通过。
范围, InRange:
首先为Patient类添加一个方法,病人吃饭之后血糖升高:
public void HaveDinner() { var random = new Random(); _bloodSugar += (float)random.Next(1, 1000) / 100; // 应该是1000 } |
添加test:
[Fact] public void BloodSugarIncreaseAfterDinner() { var p = new Patient(); p.HaveDinner(); // Assert.InRange<float>(p.BloodSugar, 5, 6); Assert.InRange(p.BloodSugar, 5, 6); } |
Build,Run Test,结果Fail:
可以看到期待的Range和实际的值,这样很好。如果你使用Assert.True(xx >= 5 && xx <= 6)的话,错误信息只能显示True或者False。
因为HaveDinner方法里,表达式的分母应该是1000,修改后,Build,Run,测试Pass。