推荐轻量友好的.NET测试断言工具Shoudly

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

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

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

为什么要Shoudly?

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

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

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

Expected 1337 but was 0

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

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

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

contestant.Points.ShouldBe(1337);

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

contestant.Points should be 1337 but was 0

Shoudly的优势

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

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

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

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

(new[] { 1, 2, 3 }).ShouldBe(new[] { 1, 2, 4 });

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

should be
[1, 2, 4]
    but was
[1, 2, 3]
    difference
[1, 2, *3*]

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

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

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

详细用法

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

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

 1 namespace Simpsons
 2 {
 3     public abstract class Pet
 4     {
 5         public abstract string Name { get; set; }
 6
 7         public override string ToString()
 8         {
 9             return Name;
10         }
11     }
12 }
13
14 namespace Simpsons
15 {
16     public class Cat : Pet
17     {
18         public override string Name { get; set; }
19     }
20 }
21
22 namespace Simpsons
23 {
24     public class Dog : Pet
25     {
26         public override string Name { get; set; }
27     }
28 }
29
30 namespace Simpsons
31 {
32     public class Person
33     {
34         public string Name { get; set; }
35         public int Salary { get; set; }
36
37
38         public override string ToString()
39         {
40             return Name;
41         }
42     }
43 }

Equality 相等

ShouldBe

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

失败信息:

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

ShouldNotBe

1 [Test]
2 public void ShouldNotBe()
3 {
4     var theSimpsonsCat = new Cat() { Name = "Santas little helper" };
5     theSimpsonsCat.Name.ShouldNotBe("Santas little helper");
6 }

失败信息:

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

ShouldBeOfType

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

失败信息:

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

Enumberable 迭代

用于对列表集合进行断言

ShouldAllBe

 1 [Test]
 2 public void IEnumerable_ShouldAllBe_Predicate()
 3 {
 4     var mrBurns = new Person() { Name = "Mr.Burns", Salary=3000000};
 5     var kentBrockman = new Person() { Name = "Homer", Salary = 3000000};
 6     var homer = new Person() { Name = "Homer", Salary = 30000};
 7     var millionares = new List<Person>() {mrBurns, kentBrockman, homer};
 8
 9     millionares.ShouldAllBe(m => m.Salary > 1000000);
10 }

失败信息:

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

ShouldContain

 1 [Test]
 2 public void IEnumerable_ShouldContain()
 3 {
 4     var mrBurns = new Person() { Name = "Mr.Burns", Salary=3000000};
 5     var kentBrockman = new Person() { Name = "Homer", Salary = 3000000};
 6     var homer = new Person() { Name = "Homer", Salary = 30000};
 7     var millionares = new List<Person>() {kentBrockman, homer};
 8
 9     millionares.ShouldContain(mrBurns);
10 }

失败信息:

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

ShouldContain(Predicate)

 1 [Test]
 2 public void IEnumerable_ShouldContain_Predicate()
 3 {
 4     var homer = new Person() { Name = "Homer", Salary = 30000};
 5     var moe = new Person() { Name = "Moe", Salary=20000};
 6     var barney = new Person() { Name = "Barney", Salary = 0};
 7     var millionares = new List<Person>() {homer, moe, barney};
 8
 9     // Check if at least one element in the IEnumerable satisfies the predicate
10     millionares.ShouldContain(m => m.Salary > 1000000);
11 }

失败信息:

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

Dynamic 动态对象

ShouldHaveProperty

1 [Test]
2 public void DynamicShouldHavePropertyTest()
3 {
4     var homerThinkingLikeFlanders = new ExpandoObject();
5     DynamicShould.HaveProperty(homerThinkingLikeFlanders, "IAmABigFourEyedLameO");
6 }

失败信息:

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

String 字符串

ShouldMatch

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

失败信息:

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

ShouldBeNullOrEmpty

1 [Test]
2 public void ShouldBeNullOrEmpty()
3 {
4     var anonymousClanOfSlackJawedTroglodytes = new Person() {Name = "The Simpsons"};
5     anonymousClanOfSlackJawedTroglodytes.Name.ShouldBeNullOrEmpty();
6 }

失败信息:

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

Dictionary 字典

ShouldNotContainKey

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

失败信息:

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

ShouldContainKeyAndValue

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

失败信息:

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

Task/Async 任务/异步

CompleteIn

 1 [Test]
 2 public void CompleteIn()
 3 {
 4     var homer = new Person() { Name = "Homer", Salary = 30000 };
 5     var denominator = 1;
 6     Should.CompleteIn(() =>
 7     {
 8         Thread.Sleep(2000);
 9         var y = homer.Salary / denominator;
10     }, TimeSpan.FromSeconds(1));
11 }

失败信息:

System.TimeoutException : The operation has timed out.

Exceptions 异常

ShouldThrow

 1 [Test]
 2 public void ShouldThrowFuncOfTask()
 3 {
 4     var homer = new Person() { Name = "Homer", Salary = 30000};
 5     var denominator = 1;
 6     Should.Throw<DivideByZeroException>(() =>
 7     {
 8         var task = Task.Factory.StartNew(() => { var y = homer.Salary/denominator; });
 9         return task;
10     });
11 }

失败信息:

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

ShouldNotThrow(Func<Task>)

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

 1 [Test]
 2 public void ShouldNotThrowFuncOfTask()
 3 {
 4     var homer = new Person() { Name = "Homer", Salary = 30000};
 5     var denominator = 0;
 6     Should.NotThrow(() =>
 7     {
 8         var task = Task.Factory.StartNew(() => { var y = homer.Salary/denominator; });
 9         return task;
10     });
11 }

失败信息:

Shouldly.ChuckedAWobbly :
    Should
        not throw
    System.DivideByZeroException
        but does 
时间: 2024-08-24 15:17:37

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

.NET测试断言工具Shouldly

.NET测试断言工具Shouldly .NET测试 Shouldly在GitHub的开源地址:https://github.com/shouldly/shouldly Shouldly的官方文档:http://docs.shouldly-lib.net/ Shouldly断言框架提供了相等.迭代.动态变量.字符串.字典.任务/异步,以及异常等多方面的支持. Equality 相等 其中之一 ShouldBeOneOf ShouldNotBeOneOf 大于 ShouldBeGreaterThan

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

最近正在使用的本地bug管理软件又出问题了,已经记不清这是第几次了,每次出现问题都要耗费大量的时间精力去网上寻找解决方案,劳心劳力.为了避免再次出现这样的情况,我决定从线下转到线上,使用轻量型的在线bug管理工具,在选择工具时有以下几个要求: 1.不用在本地安装部署,配置环境,即开即用: 2.方便bug管理,bug属性设置: 3.可以跨团队沟通,方便和开发协作: 4.随时掌握bug状态,修复进展等. 花费了一个星期的时间试用了五款在线bug管理工具,其评测结果如下: 1.   Teambitio

OWIN轻量型框架介绍

OWIN轻量型框架介绍 阅读目录 引言 框架的特色 如何启动 各项功能 静态路由的3种写法 伪静态路由的支持 处理Form表单提交的文件 流式处理Post请求的数据 多种请求类型自动识别 响应处理 请求响应上下文 自定义默认处理函数 内置各种便捷函数 复合类型的请求处理 框架的扩展 静态内容的支持 跨域Post的支持 基础类型继承灵活处理 尾声 回到顶部 引言 什么是OWIN,我就不介绍了,请自行搜索,这里主要是介绍自行开发的OWIN框架的特点和用法.由于.NET的web框架都比较庞大,导致性能

基于netty轻量的高性能分布式RPC服务框架forest&lt;下篇&gt;

基于netty轻量的高性能分布式RPC服务框架forest<上篇> 文章已经简单介绍了forest的快速入门,本文旨在介绍forest用户指南. 基本介绍 Forest是一套基于java开发的RPC框架,除了常规的点对点调用外,Motan还提供服务治理功能,包括服务节点的自动发现.摘除.高可用和负载均衡等. 架构概述 Forest中分为服务提供方(RPC Server),服务调用方(RPC Client)和服务注册中心(Registry)三个角色. Server提供服务,向Registry注册

Vue.js:轻量高效的前端组件化方案(转载)

摘要:Vue.js通过简洁的API提供高效的数据绑定和灵活的组件系统.在前端纷繁复杂的生态中,Vue.js有幸受到一定程度的关注,目前在GitHub上已经有5000+的star.本文将从各方面对Vue.js做一个深入的介绍. Vue.js 是我在2014年2月开源的一个前端开发库,通过简洁的 API 提供高效的数据绑定和灵活的组件系统.在前端纷繁复杂的生态中,Vue.js有幸受到一定程度的关注,目前在 GitHub上已经有5000+的star.本文将从各方面对Vue.js做一个深入的介绍. 开发

Go ADM,Go语言轻量ORM

厚颜无耻的推荐一下,go-adm,Go语言轻量ORM.0.1.0分支已经实现类面向对象的操作方式,说明请戳这里. 目前的总体特性已经完成,暂时不会增加其他的特性,待0.1.0的面向对象化的特性测试完善,会合并到主分支中. 艾玛,不要打我,就是推荐一下,不要打脸-- 咳,Go的确吊炸天,我计划将agimvc也搬到Golang上,不过mvc这个词可以丢掉了.

巧用命令行工具UCloud CLI,轻量操作API管理云资源

截止目前,UCloud已提供Python/Java/Golang等不同语言的API SDK.为进一步降低用户的运维人力投入,又推出了基于Golang SDK的命令行工具CLI(Command Line Interface),提供轻量化的API命令行调用方式,并在GitHub开源(https://github.com/ucloud/ucloud-cli).CLI的命令行交互方式更符合研发运维的操作习惯,并且一些典型使用场景通过CLI也更容易代码化的沉淀和维护. 下面是一些用户遇到的实际场景, 用C

全平台轻量开源verilog仿真工具iverilog+GTKWave使用教程

前言 如果你只是想检查Verilog文件的语法是否有错误,然后进行一些基本的时序仿真,那么Icarus Verilog 就是一个不错的选择.相比于各大FPGA厂商的IDE几个G的大小,Icarus Verilog 显得极其小巧,最新版安装包大小仅有17MB,支持全平台:Windows+Linux+MacOS,并且源代码开源.本文将介绍如何使用Icarus Verilog来进行verilog文件的编译和仿真. 关于 Icarus Verilog Icarus Verilog是一个轻量.免费.开源的

轻量的Memcached代理Twemproxy的部署

轻量的Memcached代理Twemproxy的部署 Twemproxy(又称为nutcracker)是一个轻量级的Redis和Memcached代理,主要用来减少对后端缓存服务器的连接数.由Twitter开源出来的缓存服务器集群管理工具,主要用来弥补Redis和Memcached对集群(cluster)管理指出的不足. Twemproxy是一个快速的单线程代理程序,支持Memcached ASCII协议和更新的Redis协议. Twemproxy最了不起的地方就在于它能在节点失败的时候卸载它,