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


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

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

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

    为什么要Shouldly?

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

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

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

    Expected 1337 but was 0

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

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

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

    contestant.Points.ShouldBe(1337);

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

    contestant.Points should be 1337 but was 0

    Shouldly的优势

    下面的一个例子将两种断言方式放在一起,通过对比更容易发现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 
  • 相关阅读:
    读者试读怎么评价这本书
    性能优化是数据库应用的核心问题
    ExtJS 4 Grid组件
    发挥你的SATA硬盘
    MOSS 修改了服务器账号密码后的问题
    速度真快,ExtJS第5个预览版发布
    Ext JS 4 Preview Release 4发布
    显卡性能排行榜
    手机操作系统发展史
    程序解读身份证的密码
  • 原文地址:https://www.cnblogs.com/defzhu/p/4841289.html
Copyright © 2020-2023  润新知