Assert Null值
[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); } |
有两个方法, Assert.Null 和 Assert.NotNull, 直接传入期待即可.
测试会Pass的.
集合 Collection Assert
修改一下被测试类, 添加一个集合属性, 并赋值:
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; } } |
测试是否包含某个元素, Assert.Contains():
[Fact] public void HaveScrewdriver() { var plumber = new Plumber(); Assert.Contains("螺丝刀", plumber.Tools); } |
Build, Run Test, 结果Pass.
修改一下名字, 让其Fail:
这个失败信息还是很详细的.
相应的还有一个Assert.DoesNotContain()方法, 测试集合是否不包含某个元素.
[Fact] public void NotHaveKeyboard() { var plumber = new Plumber(); Assert.DoesNotContain("键盘", plumber.Tools); } |
这个test也会pass.
Predicate:
测试一下集合中是否包含符合某个条件的元素:
[Fact] public void HaveAtLeastOneScrewdriver() { var plumber = new Plumber(); Assert.Contains(plumber.Tools, t => t.Contains("螺丝刀")); } |
使用的是Assert.Contains的一个overload方法, 它的第一个参数是集合, 第二个参数是Predicate.
Build, Run Test, 会Pass的.
比较集合相等:
添加Test:
[Fact] public void HaveAllTools() { var plumber = new Plumber(); var expectedTools = new [] { "螺丝刀", "扳子", "钳子" }; Assert.Equal(expectedTools, plumber.Tools); } |
注意, Plumber的tools类型是List, 这里的expectedTools类型是array.
这个test 仍然会Pass.
如果修改一个元素, 那么测试会Fail, 信息如下:
Assert针对集合的每个元素:
如果想对集合的每个元素进行Assert, 当然可以通过循环来Assert了, 但是更好的写法是调用Assert.All()方法:
[Fact] public void HaveNoEmptyDefaultTools() { var plumber = new Plumber(); Assert.All(plumber.Tools, t => Assert.False(string.IsNullOrEmpty(t))); } |
这个测试会Pass.
如果在被测试类的Tools属性添加一个空字符串, 那么失败信息会是:
这里写到, 4个元素里面有1个没有pass.
针对Object类型的Assert
首先再添加一个Programmer类:
public class Programmer : Worker { public override double TotalReward => 1000; public override double Hours => 3.5; } |
然后建立一个WorkerFactory:
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 }; } } } |
判断是否是某个类型 Assert.IsType<Type>(xx):
建立一个测试类 WorkerShould.cs和一个test:
namespace Hospital.Tests { public class WorkerShould { [Fact] public void CreatePlumberByDefault() { var factory = new WorkerFactory(); Worker worker = factory.Create("Nick"); Assert.IsType<Plumber>(worker); } } } |
Build, Run Test: 结果Pass.
相应的, 还有一个Assert.IsNotType<Type>(xx)方法.
利用Assert.IsType<Type>(xx)的返回值, 它会返回Type(xx的)的这个实例, 添加个一test:
[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); } |
Build, Run Tests: 结果Pass.
Assert针对父类:
写这样一个test, 创建的是一个promgrammer, Assert的类型是它的父类Worker:
[Fact] public void CreateProgrammer_AssertAssignableTypes() { var factory = new WorkerFactory(); Worker worker = factory.Create("Nick", isProgrammer: true); Assert.IsType<Worker>(worker); } |
这个会Fail:
这时就应该使用这个方法, Assert.IsAssignableFrom<祖先类>(xx):
[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针对对象的实例
判断两个引用是否指向不同的实例 Assert.NotSame(a, b):
[Fact] public void CreateSeperateInstances() { var factory = new WorkerFactory(); var p1 = factory.Create("Nick"); var p2 = factory.Create("Nick"); Assert.NotSame(p1, p2); } |
由工厂创建的两个对象是不同的实例, 所以这个test会Pass.
相应的还有个Assert.Same(a, b) 方法.
Assert 异常
为WorkFactory先添加一个异常处理:
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 }; } } } |
如果在test执行代码时抛出异常的话, 那么test会直接fail掉.
所以应该使用Assert.Throws<ArgumentNullException>(...)方法来Assert是否抛出了特定类型的异常.
添加一个test:
[Fact] public void NotAllowNullName() { var factory = new WorkerFactory(); // var p = factory.Create(null); // 这个会失败 Assert.Throws<ArgumentNullException>(() => factory.Create(null)); } |
注意不要直接运行会抛出异常的代码. 应该在Assert.Throws<ET>()的方法里添加lambda表达式来调用方法.
这样的话就会pass.
如果被测试代码没有抛出异常的话, 那么test会fail的. 把抛异常代码注释掉之后再Run:
更具体的, 还可以指定参数的名称:
[Fact] public void NotAllowNullName() { var factory = new WorkerFactory(); // Assert.Throws<ArgumentNullException>(() => factory.Create(null)); Assert.Throws<ArgumentNullException>("name", () => factory.Create(null)); } |
这里就是说异常里应该有一个叫name的参数.
Run: Pass.
如果把"name"改成"isProgrammer", 那么这个test会fail:
利用Assert.Throws<ET>()的返回结果, 其返回结果就是这个抛出的异常实例.
[Fact] public void NotAllowNullNameAndUseReturnedException() { var factory = new WorkerFactory(); ArgumentNullException ex = Assert.Throws<ArgumentNullException>(() => factory.Create(null)); Assert.Equal("name", ex.ParamName); } |
Assert Events 是否发生(Raised)
回到之前的Patient类, 添加如下代码:
public void Sleep() { OnPatientSlept(); } public event EventHandler<EventArgs> PatientSlept; protected virtual void OnPatientSlept() { PatientSlept?.Invoke(this, EventArgs.Empty); } |
然后回到PatientShould.cs添加test:
[Fact] public void RaiseSleptEvent() { var p = new Patient(); Assert.Raises<EventArgs>( handler => p.PatientSlept += handler, handler => p.PatientSlept -= handler, () => p.Sleep()); } |
Assert.Raises<T>()第一个参数是附加handler的Action, 第二个参数是分离handler的Action, 第三个Action是触发event的代码.
Build, Run Test: Pass.
如果注释掉Patient类里Sleep()方法内部那行代码, 那么test会fail:
针对INotifyPropertyChanged的特殊Assert:
修改Patient代码:
View Code
添加一个Test:
[Fact] public void RaisePropertyChangedEvent() { var p = new Patient(); Assert.PropertyChanged(p, "BloodSugar", () => p.HaveDinner()); } |
针对INotifyPropertyChanged, 可以使用Assert.PropertyChanged(..) 这个专用的方法来断定PropertyChanged的Event是否被触发了。
Build, Run Tests: Pass.
到目前为止, 介绍的都是入门级的内容。
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。