基于属性的测试在C#中:捕获单元测试遗漏的Bug

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

您的单元测试在撒谎(而您心知肚明)

\n\n

想象一下:您写了50个单元测试,全部通过,覆盖率90%。您很满意。但一周后,生产环境崩溃了。原来,您的方法SortArray()对于[1, 2, 3]和[3, 1, 2]运行良好,但在[int.MaxValue, -1, 0]上崩溃了。熟悉吗?

\n\n

经典单元测试的问题在于开发者的偏见。您编写的测试只针对您预期的数据。黑客、真实用户和随机的边界条件遵循着不同的规则。

\n\n

这时,基于属性的测试在C#中(或基于属性的测试)登场了。与其检查“如果A,则B”,您描述的是属性,这些属性必须始终为真。而框架会自动生成数百个随机输入数据并验证您的断言。

\n\n

在本文中,我们将探讨如何通过FsCheck库在C#中引入基于属性的测试,编写真实示例,并看看它如何捕获普通测试遗漏的Bug。

\n\n

什么是基于属性的测试?

\n\n

基于属性的测试是一种方法,您在其中制定程序的不变量(属性)。例如:

\n
    \n
  • “排序后数组长度不变”
  • \n
  • “反转字符串的逆序得到原始字符串”
  • \n
  • “两个正数之和始终为正”
  • \n
\n\n

框架(例如FsCheck)会生成数十甚至数百个随机输入数据,并检查该属性是否对所有数据都成立。如果找到反例,测试失败,您会得到导致代码崩溃的具体值。

\n\n

与单元测试不同,您不会写Assert.AreEqual(3, Add(1,2))。您会写Assert(x + y == y + x)——然后测试数千对数字的交换律。

\n\n

与普通单元测试的区别

\n\n\n\n\n\n\n\n
单元测试基于属性的测试
检查具体示例检查通用法则
数据由开发者指定数据由框架生成
遗漏边界情况自动探索边界
易于阅读需要抽象思维
\n\n

FsCheck库:您的主要工具

\n\n

在C#世界中,基于属性测试的事实标准是FsCheck(最初是Haskell库QuickCheck的移植)。它与xUnit、NUnit和MSTest集成。

\n\n

安装很简单:

\n
dotnet add package FsCheck\ndotnet add package FsCheck.Xunit  // 用于与xUnit集成
\n\n

FsCheck能够为基本类型(int、string、float)、列表、数组、元组甚至自定义类型(通过自定义生成器)生成随机数据。

\n\n

编写第一个基于属性的C#测试

\n\n

我们从简单的开始:验证整数加法运算的交换律。

\n\n
using FsCheck;\nusing FsCheck.Xunit;\n\npublic class AdditionTests\n{\n    [Property]\n    public bool Commutative_Addition(int x, int y)\n    {\n        return x + y == y + x;\n    }\n}
\n\n

运行测试。FsCheck会生成100对随机的int(默认情况下)并检查该属性。如果没有找到反例,测试通过。如果我们犯了错误(例如,写了x - y == y - x),测试会失败并给出具体示例:(5, 3)——因为5 - 3 != 3 - 5。

\n\n

解析代码

\n
    \n
  • [Property]——属性,告诉FsCheck这是一个基于属性的测试。
  • \n
  • 方法返回bool——如果返回false,测试失败。
  • \n
  • 参数int x, int y——FsCheck会自动填入随机值

博客

书籍推荐