第一部分: http://www.cnblogs.com/cgzl/p/8283610.html
下面有一点点内容是重叠的....
String Assert
测试 string 是否相等:
然后你需要 Build 一下,这样 VS Test Explorer 才能发现新的 test.
[Fact]
public void CalculateFullName()
{
var p = new Patient
{
FirstName = "Nick",
LastName = "Carter"
};
Assert.Equal("Nick Carter", p.FullName);
}
运行测试,结果 Pass:
同样改一下 Patient 类(别忘了 Build 一下),让结果失败:
从失败信息可以看到期待值和实际值.
Build,然后 Run Test,结果 Pass:
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);
}
忽略大小写 ignoreCase:
string 默认的 Assert 是区分大小写的,这样就会失败:
可以为这些方法添加一个参数 ignoreCase 设置为 true,就会忽略大小写:
包含子字符串 Contains
Build,测试结果 Pass.
[Fact]
public void CalculateFullNameSubstring()
{
var p = new Patient
{
FirstName = "Nick",
LastName = "Carter"
};
Assert.Contains("ck Ca", p.FullName);
}
正则表达式,Matches
测试一下 First name 和 Last name 的首字母是不是大写的:
Build,测试通过.
[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);
}
数值 Assert
首先为 Patient 类添加一个 property: BloodSugar.
Build,测试通过.
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);
}
范围, InRange:
首先为 Patient 类添加一个方法,病人吃饭之后血糖升高:
添加 test:
public void HaveDinner()
{
var random = new Random();
_bloodSugar += (float)random.Next(1, 1000) / 100; // 应该是1000
}
Build,Run Test,结果 Fail:
[Fact]
public void BloodSugarIncreaseAfterDinner()
{
var p = new Patient();
p.HaveDinner();
// Assert.InRange<float>(p.BloodSugar, 5, 6);
Assert.InRange(p.BloodSugar, 5, 6);
}
可以看到期待的 Range 和实际的值,这样很好.如果你使用
Assert.True(xx >= 5 && xx <= 6)
的话,错误信息只能显示 True 或者 False.
因为 HaveDinner 方法里,表达式的分母应该是 1000,修改后,Build,Run,测试 Pass.
浮点型数值的 Assert
在被测项目添加这两个类:
然后针对 Plumber 建立一个测试类 PlumberShould.cs, 并建立第一个 test:
namespace Hospital
{
public abstract class Worker
{
public string Name { get; set; }
public abstract double TotalReward { get; }
public abstract double Hours { get; }
public double Salary => TotalReward / Hours;
}
public class Plumber : Worker
{
public override double TotalReward => 200;
public override double Hours => 3;
}
}
Build 项目, 然后再 Test Explorer 里面选择按 Class 分类显示 Tests:
namespace Hospital.Tests
{
public class PlumberShould
{
[Fact]
public void HaveCorrectSalary()
{
var plumber = new Plumber();
Assert.Equal(66.666, plumber.Salary);
}
}
}
Run Selected Test, 结果会失败:
这是一个精度的问题.
在 Assert.Equal 方法, 可以添加一个 precision 参数, 设置精度为 3:
[Fact]
public void HaveCorrectSalary()
{
var plumber = new Plumber();
Assert.Equal(66.666, plumber.Salary, precision: 3);
}
Build, Run Test:
因为有四舍五入的问题, 所以 test 仍然 fail 了.
所以还需要改一下:
这次会 pass 的:
[Fact]
public void HaveCorrectSalary()
{
var plumber = new Plumber();
Assert.Equal(66.667, plumber.Salary, precision: 3);
}
Assert Null 值
有两个方法, Assert.Null 和 Assert.NotNull, 直接传入期待即可.
[Fact]
public void NotHaveNameByDefault()
{
var plumber = new Plumber();
Assert.Null(plumber.Name);
}
[Fact]
public void HaveNameValue()
{
var plumber = new Plumber
{
Name = "Brian"
};
Assert.NotNull(plumber.Name);
}
测试会 Pass 的.
集合 Collection Assert
修改一下被测试类, 添加一个集合属性, 并赋值:
测试是否包含某个元素, Assert.Contains():
namespace Hospital
{
public abstract class Worker
{
public string Name { get; set; }
public abstract double TotalReward { get; }
public abstract double Hours { get; }
public double Salary => TotalReward / Hours;
public List<string> Tools { get; set; }
}
public class Plumber : Worker
{
public Plumber()
{
Tools = new List<string>()
{
"螺丝刀",
"扳子",
"钳子"
};
}
public override double TotalReward => 200;
public override double Hours => 3;
}
}
Build, Run Test, 结果 Pass.
[Fact]
public void HaveScrewdriver()
{
var plumber = new Plumber();
Assert.Contains("螺丝刀", plumber.Tools);
}
修改一下名字, 让其 Fail:
这个失败信息还是很详细的.
相应的还有一个 Assert.DoesNotContain() 方法, 测试集合是否不包含某个元素.
这个 test 也会 pass.
[Fact]
public void NotHaveKeyboard()
{
var plumber = new Plumber();
Assert.DoesNotContain("键盘", plumber.Tools);
}
Predicate:
测试一下集合中是否包含符合某个条件的元素:
使用的是 Assert.Contains 的一个 overload 方法, 它的第一个参数是集合, 第二个参数是 Predicate.
[Fact]
public void HaveAtLeastOneScrewdriver()
{
var plumber = new Plumber();
Assert.Contains(plumber.Tools, t => t.Contains("螺丝刀"));
}
Build, Run Test, 会 Pass 的.
比较集合相等:
添加 Test:
注意, Plumber 的 tools 类型是 List, 这里的 expectedTools 类型是 array.
[Fact]
public void HaveAllTools()
{
var plumber = new Plumber();
var expectedTools = new []
{
"螺丝刀",
"扳子",
"钳子"
};
Assert.Equal(expectedTools, plumber.Tools);
}
这个 test 仍然会 Pass.
如果修改一个元素, 那么测试会 Fail, 信息如下:
Assert 针对集合的每个元素:
如果想对集合的每个元素进行 Assert, 当然可以通过循环来 Assert 了, 但是更好的写法是调用 Assert.All() 方法:
这个测试会 Pass.
[Fact]
public void HaveNoEmptyDefaultTools()
{
var plumber = new Plumber();
Assert.All(plumber.Tools, t => Assert.False(string.IsNullOrEmpty(t)));
}
如果在被测试类的 Tools 属性添加一个空字符串, 那么失败信息会是:
这里写到, 4 个元素里面有 1 个没有 pass.
针对 Object 类型的 Assert
首先再添加一个 Programmer 类:
然后建立一个 WorkerFactory:
public class Programmer : Worker
{
public override double TotalReward => 1000;
public override double Hours => 3.5;
}
判断是否是某个类型 Assert.IsType<Type>(xx):
namespace Hospital {
public class WorkerFactory {
public Worker Create(string name, bool isProgrammer = false) {
if (isProgrammer) {
return new Programmer {
Name = name
};
}
return new Plumber {
Name = name
};
}
}
}
建立一个测试类 WorkerShould.cs 和一个 test:
Build, Run Test: 结果 Pass.
namespace Hospital.Tests
{
public class WorkerShould
{
[Fact]
public void CreatePlumberByDefault()
{
var factory = new WorkerFactory();
Worker worker = factory.Create("Nick");
Assert.IsType<Plumber>(worker);
}
}
}
相应的, 还有一个 Assert.IsNotType<Type>(xx) 方法.
利用 Assert.IsType<Type>(xx) 的返回值, 它会返回 Type(xx 的) 的这个实例, 添加个一 test:
Build, Run Tests: 结果 Pass.
[Fact]
public void CreateProgrammerAndCastReturnedType()
{
var factory = new WorkerFactory();
Worker worker = factory.Create("Nick", isProgrammer: true);
Programmer programmer = Assert.IsType<Programmer>(worker);
Assert.Equal("Nick", programmer.Name);
}
Assert 针对父类:
写这样一个 test, 创建的是一个 promgrammer, Assert 的类型是它的父类 Worker:
这个会 Fail:
[Fact]
public void CreateProgrammer_AssertAssignableTypes()
{
var factory = new WorkerFactory();
Worker worker = factory.Create("Nick", isProgrammer: true);
Assert.IsType<Worker>(worker);
}
这时就应该使用这个方法, Assert.IsAssignableFrom <祖先类>(xx):
Assert 针对对象的实例
[Fact] public void CreateProgrammer_AssertAssignableTypes() {
var factory = new WorkerFactory();
Worker worker = factory.Create("Nick", isProgrammer: true);
Assert.IsAssignableFrom < Worker > (worker);
}
Build,
Run Tests: Pass.
判断两个引用是否指向不同的实例 Assert.NotSame(a, b):
由工厂创建的两个对象是不同的实例, 所以这个 test 会 Pass.
[Fact]
public void CreateSeperateInstances()
{
var factory = new WorkerFactory();
var p1 = factory.Create("Nick");
var p2 = factory.Create("Nick");
Assert.NotSame(p1, p2);
}
相应的还有个 Assert.Same(a, b) 方法.
Assert 异常
为 WorkFactory 先添加一个异常处理:
如果在 test 执行代码时抛出异常的话, 那么 test 会直接 fail 掉.
namespace Hospital {
public class WorkerFactory {
public Worker Create(string name, bool isProgrammer = false) {
if (name == null) {
throw new ArgumentNullException(nameof(name));
}
if (isProgrammer) {
return new Programmer {
Name = name
};
}
return new Plumber {
Name = name
};
}
}
}
所以应该使用 Assert.Throws<ArgumentNullException>(...) 方法来 Assert 是否抛出了特定类型的异常.
添加一个 test:
注意不要直接运行会抛出异常的代码. 应该在 Assert.Throws() 的方法里添加 lambda 表达式来调用方法.
[Fact]
public void NotAllowNullName()
{
var factory = new WorkerFactory();
// var p = factory.Create(null); // 这个会失败
Assert.Throws<ArgumentNullException>(() => factory.Create(null));
}
这样的话就会 pass.
如果被测试代码没有抛出异常的话, 那么 test 会 fail 的. 把抛异常代码注释掉之后再 Run:
更具体的, 还可以指定参数的名称:
这里就是说异常里应该有一个叫 name 的参数.
[Fact]
public void NotAllowNullName()
{
var factory = new WorkerFactory();
// Assert.Throws<ArgumentNullException>(() => factory.Create(null));
Assert.Throws<ArgumentNullException>("name", () => factory.Create(null));
}
Run: Pass.
如果把 "name" 改成 "isProgrammer", 那么这个 test 会 fail:
利用 Assert.Throws<ET>() 的返回结果, 其返回结果就是这个抛出的异常实例.
Assert Events 是否发生 (Raised)
[Fact]
public void NotAllowNullNameAndUseReturnedException()
{
var factory = new WorkerFactory();
ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => factory.Create(null));
Assert.Equal("name", ex.ParamName);
}
回到之前的 Patient 类, 添加如下代码:
然后回到 PatientShould.cs 添加 test:
public void Sleep()
{
OnPatientSlept();
}
public event EventHandler<EventArgs> PatientSlept;
protected virtual void OnPatientSlept()
{
PatientSlept?.Invoke(this, EventArgs.Empty);
}
Assert.Raises<T>() 第一个参数是附加 handler 的 Action, 第二个参数是分离 handler 的 Action, 第三个 Action 是触发 event 的代码.
[Fact]
public void RaiseSleptEvent()
{
var p = new Patient();
Assert.Raises<EventArgs>(
handler => p.PatientSlept += handler,
handler => p.PatientSlept -= handler,
() => p.Sleep());
}
Build, Run Test: Pass.
如果注释掉 Patient 类里 Sleep() 方法内部那行代码, 那么 test 会 fail:
针对 INotifyPropertyChanged 的特殊 Assert:
修改 Patient 代码:
View Code
添加一个 Test:
针对 INotifyPropertyChanged, 可以使用 Assert.PropertyChanged(..) 这个专用的方法来断定 PropertyChanged 的 Event 是否被触发了.
[Fact]
public void RaisePropertyChangedEvent()
{
var p = new Patient();
Assert.PropertyChanged(p, "BloodSugar", () => p.HaveDinner());
}
Build, Run Tests: Pass.
到目前为止, 介绍的都是入门级的内容.
接下来要介绍的是稍微进阶一点的内容了.
来源: https://www.cnblogs.com/cgzl/p/8287588.html