Shouldly是一个轻量的断言(Assertion)框架,用于补充.NET框架下的测试工具。Shouldly将焦点放在当断言失败时如何简单精准的给出很好的错误信息。

Shouldly在GitHub的开源地址:https://github.com/shouldly/shouldly

Shouldly的官方文档:http://docs.shouldly-lib.net/

为什么要Shouldly?

我们知道通常测试代码中一个断言是这样写的:

Assert.That(contestant.Points, Is.EqualTo());

当断言失败的时候,我们会得到这样的信息:

Expected  but was 

这样的信息让人烦恼的是,它够简练,但显然信息不够充分,可读性不高。

Shouldly充分利用了.NET框架的扩展方法,提供了更友好的处理方式。

用Shouldly编写的断言是这样的:

contestant.Points.ShouldBe();

其中ShouldBe是一个扩展方法,也可以认为是一个易于理解的语法词汇。当断言失败的时候,我们会得到这样的信息:

contestant.Points should be  but was 

Shouldly的优势

下面的一个例子将两种断言方式放在一起,通过对比更容易发现Shouldly的好处:

Assert.That(map.IndexOfValue("boo"), Is.EqualTo());   // -> Expected 2 but was 1
map.IndexOfValue("boo").ShouldBe(); // -> map.IndexOfValue("boo") should be 2 but was 1

Shouldly使用ShouldBe语句中的变量来报告错误,这使得更容易诊断。

另一个例子,如果你需要比较两个集合,采用Shouldly写法如下:

(new[] { , ,  }).ShouldBe(new[] { , ,  });

显然断言会失败,因为这两个集合并不相同。但Shouldly不仅如此,它会告诉你两个集合的差异所在:

should be
[, , ]
but was
[, , ]
difference
[, , **]

如果你想检查一个特殊的方法调用,它会或者不会抛出异常,它可以简单地这样写:

Should.Throw<ArgumentOutOfRangeException>(() => widget.Twist(-));

这样就可以接触到异常,借此帮助你调试出潜在的异常来源。

详细用法

Shouldly断言框架提供了相等、迭代、动态变量、字符串、字典、任务/异步,以及异常等多方面的支持。要详细的了解这些可以访问Shouldly的官方文档:http://docs.shouldly-lib.net/

后续的断言例子基于下面的范例类:

 namespace Simpsons
{
public abstract class Pet
{
public abstract string Name { get; set; } public override string ToString()
{
return Name;
}
}
} namespace Simpsons
{
public class Cat : Pet
{
public override string Name { get; set; }
}
} namespace Simpsons
{
public class Dog : Pet
{
public override string Name { get; set; }
}
} namespace Simpsons
{
public class Person
{
public string Name { get; set; }
public int Salary { get; set; } public override string ToString()
{
return Name;
}
}
}

Equality 相等

ShouldBe

 [Test]
public void ShouldBe()
{
var theSimpsonsCat = new Cat() { Name = "Santas little helper" };
theSimpsonsCat.Name.ShouldBe("Snowball 2");
}

失败信息:

Shouldly.ChuckedAWobbly :
theSimpsonsCat.Name
should be
"Snowball 2"
but was
"Santas little helper"

ShouldNotBe

 [Test]
public void ShouldNotBe()
{
var theSimpsonsCat = new Cat() { Name = "Santas little helper" };
theSimpsonsCat.Name.ShouldNotBe("Santas little helper");
}

失败信息:

Shouldly.ChuckedAWobbly :
theSimpsonsCat.Name
should not be
"Santas little helper"
but was
"Santas little helper"

ShouldBeOfType

 [Test]
public void ShouldBeOfType()
{
var theSimpsonsDog = new Cat() { Name = "Santas little helper" };
theSimpsonsDog.ShouldBeOfType<Dog>();
}

失败信息:

Shouldly.ChuckedAWobbly :
theSimpsonsDog
should be of type
Simpsons.Dog
but was
Simpsons.Cat

Enumberable 迭代

用于对列表集合进行断言

ShouldAllBe

 [Test]
public void IEnumerable_ShouldAllBe_Predicate()
{
var mrBurns = new Person() { Name = "Mr.Burns", Salary=};
var kentBrockman = new Person() { Name = "Homer", Salary = };
var homer = new Person() { Name = "Homer", Salary = };
var millionares = new List<Person>() {mrBurns, kentBrockman, homer}; millionares.ShouldAllBe(m => m.Salary > );
}

失败信息:

Shouldly.ChuckedAWobbly :
millionares
should all be an element satisfying the condition
(m.Salary > )
but does not

ShouldContain

 [Test]
public void IEnumerable_ShouldContain()
{
var mrBurns = new Person() { Name = "Mr.Burns", Salary=};
var kentBrockman = new Person() { Name = "Homer", Salary = };
var homer = new Person() { Name = "Homer", Salary = };
var millionares = new List<Person>() {kentBrockman, homer}; millionares.ShouldContain(mrBurns);
}

失败信息:

Shouldly.ShouldAssertException :
millionares
should contain
Mr.Burns
but does not

ShouldContain(Predicate)

 [Test]
public void IEnumerable_ShouldContain_Predicate()
{
var homer = new Person() { Name = "Homer", Salary = };
var moe = new Person() { Name = "Moe", Salary=};
var barney = new Person() { Name = "Barney", Salary = };
var millionares = new List<Person>() {homer, moe, barney}; // Check if at least one element in the IEnumerable satisfies the predicate
millionares.ShouldContain(m => m.Salary > );
}

失败信息:

Shouldly.ChuckedAWobbly :
millionares
should contain an element satisfying the condition
(m.Salary > )
but does not

Dynamic 动态对象

ShouldHaveProperty

 [Test]
public void DynamicShouldHavePropertyTest()
{
var homerThinkingLikeFlanders = new ExpandoObject();
DynamicShould.HaveProperty(homerThinkingLikeFlanders, "IAmABigFourEyedLameO");
}

失败信息:

Shouldly.ChuckedAWobbly :
Dynamic Object
"homerThinkingLikeFlanders"
should contain property
"IAmABigFourEyedLameO"
but does not.

String 字符串

ShouldMatch

 [Test]
public void ShouldMatch()
{
var simpsonDog = new Dog() { Name = "Santas little helper" };
simpsonDog.Name.ShouldMatch("Santas [lL]ittle Helper");
}

失败信息:

Shouldly.ChuckedAWobbly :
simpsonDog.Name
should match
"Santas [lL]ittle Helper"
but was
"Santas little helper"

ShouldBeNullOrEmpty

 [Test]
public void ShouldBeNullOrEmpty()
{
var anonymousClanOfSlackJawedTroglodytes = new Person() {Name = "The Simpsons"};
anonymousClanOfSlackJawedTroglodytes.Name.ShouldBeNullOrEmpty();
}

失败信息:

Shouldly.ChuckedAWobbly :
anonymousClanOfSlackJawedTroglodytes.Name
should be null or empty

Dictionary 字典

ShouldNotContainKey

 [Test]
public void ShouldNotContainKey()
{
var websters = new Dictionary<string, string>();
websters.Add("Chazzwazzers", "What Australians would have called a bull frog."); websters.ShouldNotContainKey("Chazzwazzers");
}

失败信息:

Shouldly.ChuckedAWobbly :
Dictionary
"websters"
should not contain key
"Chazzwazzers"
but does

ShouldContainKeyAndValue

 [Test]
public void ShouldNotContainKey()
{
var websters = new Dictionary<string, string>();
websters.Add("Chazzwazzers", "What Australians would have called a bull frog."); websters.ShouldNotContainKey("Chazzwazzers");
}

失败信息:

Shouldly.ChuckedAWobbly :
Dictionary
"websters"
should not contain key
"Chazzwazzers"
but does

Task/Async 任务/异步

CompleteIn

 [Test]
public void CompleteIn()
{
var homer = new Person() { Name = "Homer", Salary = };
var denominator = ;
Should.CompleteIn(() =>
{
Thread.Sleep();
var y = homer.Salary / denominator;
}, TimeSpan.FromSeconds());
}

失败信息:

System.TimeoutException : The operation has timed out.

Exceptions 异常

ShouldThrow

 [Test]
public void ShouldThrowFuncOfTask()
{
var homer = new Person() { Name = "Homer", Salary = };
var denominator = ;
Should.Throw<DivideByZeroException>(() =>
{
var task = Task.Factory.StartNew(() => { var y = homer.Salary/denominator; });
return task;
});
}

失败信息:

Shouldly.ChuckedAWobbly :
Should
throw
System.DivideByZeroException
but does not

ShouldNotThrow(Func<Task>)

这个方法支持异步方法,并且会等待操作执行直到完成。

 [Test]
public void ShouldNotThrowFuncOfTask()
{
var homer = new Person() { Name = "Homer", Salary = };
var denominator = ;
Should.NotThrow(() =>
{
var task = Task.Factory.StartNew(() => { var y = homer.Salary/denominator; });
return task;
});
}

失败信息:

Shouldly.ChuckedAWobbly :
Should
not throw
System.DivideByZeroException
but does

推荐轻量友好的.NET测试断言工具Shouldly的更多相关文章

  1. .NET测试断言工具Shouldly

    .NET测试断言工具Shouldly .NET测试 Shouldly在GitHub的开源地址:https://github.com/shouldly/shouldly Shouldly的官方文档:ht ...

  2. 推荐轻量高效无依赖的开源JS插件和库

    目录 图片 布局 音频视频 编辑器 轮播图 弹出层 表单 存储 动画 时间 其它 CDN 图片 baguetteBox.js - 是一个简单易用的响应式图像灯箱效果脚本.demo Lightgalle ...

  3. RestTemplate---Spring提供的轻量Http Rest 风格API调用工具

    前言 今天在学习Spring Cloud的过程中无意发现了 RestTemplate 这个Spring 提供的Http Rest风格接口之间调用的模板工具类,感觉比Apache提供的HttpClien ...

  4. 五款轻量型bug管理工具横向测评

    五款轻量型bug管理工具横向测评 最近正在使用的本地bug管理软件又出问题了,已经记不清这是第几次了,每次出现问题都要耗费大量的时间精力去网上寻找解决方案,劳心劳力.为了避免再次出现这样的情况,我决定 ...

  5. 推荐一个简单、轻量、功能非常强大的C#/ASP.NET定时任务执行管理器组件–FluentScheduler定时器

    在C#WINFORM或者是ASP.NET的WEB应用程序中,根据各种定时任务的需求,比如:每天的数据统计,每小时刷新系统缓存等等,这个时候我们得应用到定时器这个东东. .NET Framework有自 ...

  6. Furatto – 轻量,友好的响应式前端开发框架

    Furatto 是一个基于 Bootstrap & Foundation 的前端开发框架,用于快速开发网站.这个框架采用流行的扁平化设计和响应式设计.除了布局和网格之外,所有的主要元素都有预定 ...

  7. 推荐一款轻量小众却高效免费开源windows热键脚本语言Autohotkey

    写在前面的话 Autohotkey是一款轻量小众但高效免费开源的windows热键脚本语言,游戏操纵.鼠标操作.键盘快捷重定义,快捷短语等等,只有你想不到,没有它做不到,神器中的神器呀,相见恨晚. 安 ...

  8. Vue.js:轻量高效的前端组件化方案

    转发一篇尤老师对vue.js的介绍,了解vue.js的来龙去脉.不过现在已经是2.0了,也有添加一些新的东西,当然有些东西也改了. Vue.js:轻量高效的前端组件化方案 Vue.js 是我在2014 ...

  9. 基于netty轻量的高性能分布式RPC服务框架forest<下篇>

    基于netty轻量的高性能分布式RPC服务框架forest<上篇> 文章已经简单介绍了forest的快速入门,本文旨在介绍forest用户指南. 基本介绍 Forest是一套基于java开 ...

随机推荐

  1. JMir——Java版热血传奇2之资源文件与地图

    我虽然是90后,但是也很喜欢热血传奇2(以下简称“传奇”)这款游戏. 进入程序员行业后自己也对传奇客户端实现有所研究,现在将我的一些研究结果展示出来,如果大家有兴趣的话不妨与我交流. 项目我托管到co ...

  2. SetHandleInformation设置内核对象标志

    当父进程创建子进程时,子进程将继承父进程的内核对象.这时如果要控制子进程使用父进程的内核对象.可以使用 SetHandleInformation设置. BOOL SetHandleInformatio ...

  3. SQL1159 Initialization error with DB2 .NET Data Provider, reason code 7(问题补充)

    SQL1159 Initialization error with DB2 .NET Data Provider, reason code 7 需要注册GAC,修改注册表 IBM官方方案: http: ...

  4. Objective-C Polymorphism

    #import <Foundation/Foundation.h> @interface Shape : NSObject { CGFloat area; } -(void)printAr ...

  5. 如何对excel进行列查重

    学习了excel函数:countif.表达式:COUNTIF(数据区域,条件),作用:对数据区域内符合条件单元格计数 具体应用 在“姓名”(列A)后插入一列(列B),在B2单元格输入公式“=IF(CO ...

  6. java中JTextPane使输出字符到指定的宽度换行,并将垂直滚动条的位置移动到输出的最后位置

    SimpleAttributeSet set = new SimpleAttributeSet(); Document doc = tp.getStyledDocument(); FontMetric ...

  7. Membership三步曲

    http://www.cnblogs.com/jesse2013/p/membership-part1.html http://www.cnblogs.com/jesse2013/p/membersh ...

  8. java io系列01之 "目录"

    java io 系列目录如下: 01. java io系列01之  "目录" 02. java io系列02之 ByteArrayInputStream的简介,源码分析和示例(包括 ...

  9. Tips1:用 Export Package选项来分享你的成果

    如果你不是一个人工作,你可能需要和其他人共享一个工程文件,Unity工程文件中的一些关键元素默认是隐藏的,因此通过复制Assets文件夹的方法并不完善.Unity自带的UnityPackage格式的文 ...

  10. android resources使用总结

    http://developer.android.com/guide/topics/resources/more-resources.html http://developer.android.com ...