基于属性的测试是一种包含了明确规定了结果应该为真,且不依赖特殊用例的方法。这可以帮助你用较少的测试用例去大量输入测试函数。
Lucy Mair 在 2023NDC Oslo 大会上提到了基于属性的测试。
基于属性的测试是一种识别属性的技术,有着如下描述:“对于满足某些前提条件的所有输入,某些断言成立。”并且使用这些属性进行测试,正如 Mair 提到:
首先,你需要编写一个有着明确定义属性的函数:它可以输入前提条件,然后判断断言是否成立。然后,你使用基于属性的测试框架,它会产生大量不同的输入数据,并且运行你的函数去检查这些断言是否成立。
Mair 使用 FsCheck 和 F# 举了一个例子。不过他们也提到这个技术可以使用任何语言。
Mair 说,使用基于属性的测试最直接的好处是能够测试大量输入函数,而无需编写大量的测试用例。每一次运行基于属性的测试都会使用不同的输入,它可以让你确信你的代码在一般情况下是有效的。Mair 提到,这对于发现你从未想到过的一些边界值测试特别有价值。
基于属性的测试通过生成和测试大量案例来工作,因此比传统的单元测试需要更长的时间。Mair 提到,性能影响将根据特殊用例的不同而有所不同。
InfoQ 就基于属性的测试采访了 Lucy Mair
InfoQ: 你可以给一个属性的例子吗?
属性和函数相关,所以我们考虑一个连接了两个列表的函数concat(a,b)的情况。例如concat([0,1,2],[3,4])=[0,1,2,3,4]
我们可以写出哪些关于c=concat(a,b)的陈述呢,哪些陈述总是正确的呢?
1、c的长度=a的长度+b的长度
2、a的所有元素都会出现在c中
3、b的所有元素都会出现在c中
4、交换和一个空列表链接顺序会得到相同结果a=concat(a,[])=concat([],a)
InfoQ: 在前端开发中,我们如何进行基于属性的测试?
JavaScript在前端开发中给了基于属性的测试一些独特的挑战。
第一个问题在于,JavaScript相对宽松的运行类型意味着生成正确形状的对象用以运行测试并非易事。TypeScript在这方面也无法帮助我们;TypeScript类型仅在编译时可用,因此无法用于创建正确形状的对象。
第二个原因是前端测试时渲染组件很慢,尤其是测试用户交互。如果你的基于属性测试包含了前端渲染,它会变得慢到无法使用。
InfoQ: 基于属性的测试有哪些潜在的缺点?
对于纯函数的基于属性测试,传统的单元测试可能需要10毫秒,而基于属性的测试可能需要200毫秒,所以性能的影响可以忽略不计。然而,如果你的测试正在做“缓慢”的事情,比如组件渲染,进行API调用,或者执行密集型计算机运算,那么基于属性的测试会有大量的性能开销需要使用。
把基于属性的测试引入到现有的代码库中是一个挑战。你可能会需要在程序运行时永远不会出现且从未测试到的边界值问题。一个例子是除以一个数字,在这种情况下,零是一个无效的数字,但你可能知道这个数字永远不是零。这代表了一个在测试中编写的规范,但是在实践中很少使用。