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. Selenium自动化测试项目案例实践公开课

    Selenium自动化测试项目案例实践公开课: http://gdtesting.cn/news.php?id=55

  2. android html 与webview属性从冲突

    在最近的项目开发中,使用webview加载html页面,这样可以节省大量页面开发的时间,同时也可加快项目进度. 我们需求是需要显示商品评论,页面设计如下: 调用android代码,对于webview的 ...

  3. Ubuntu下的防火墙

    Ubuntu下的防火墙Gufw-ufw Gufw是ufw的桌面版本,网上有Gufw的安装和配置方法,但是我认为并不是很好.网上的教程也有点愚弄大众的嫌疑,因为按照他的说法,最后防火墙根本就无法自动启动 ...

  4. ASP.NET服务器控件使用之Reportviewer 报表

    http://blog.csdn.net/oemoon/article/details/7338967 http://www.cnblogs.com/emanlee/archive/2008/09/1 ...

  5. MSSQL 之事务订单存储过程

    1. 赋值   set  或者 select 运算符 2.全局,局部变量区别,生命域 (全局变量用户不能定义) 3.@@identity  返回最后插入行的标识列的列值. 4.delete 只删除了数 ...

  6. DDD:Strategic Domain Driven Design with Context Mapping

    Introduction Many approaches to object oriented modeling tend not to scale well when the application ...

  7. 介绍cms

    在这篇文章中,我们先来定义下什么是CMS(Content Management System)系统,在网站中它是如何帮你来变更内容的. 最后我将展示如何登录Umbraco系统. 简单来说CMS是一个系 ...

  8. list2json

    list2json方法 System.Web.Script.Serialization.JavaScriptSerializer serial = new System.Web.Script.Seri ...

  9. 企业云部署要如何选择IaaS PaaS和SaaS

    1为什么IaaS成了灵丹妙药   我非常惊讶,为什么很多传统企业已经接受了云计算,但接受的方式却往往不尽人意.对大多数企业来说,云计算的投入产出比相对较小,并且局限于基础设施层的环节. 就目前而言,大 ...

  10. Xcode-Xcode 7.3 解决不能自动联想问题

    一.问题: 升级Xcode 7.3 之后发现导入头文件之后,没法自动联想. 二. 解决办法: 打开Xcode --> Target --> BuildSettings --> App ...