我正在学习 QuickCheck >= 2.6 的技巧,但我不明白收缩是什么。从类型签名来看,收缩看起来更像是扩展!请照亮我:)
2 回答
当 QuickCheck 发现一个违反属性的输入时,它会首先尝试找到也违反该属性的较小输入,以便向开发人员提供有关故障性质的更好消息。
“小”的含义当然取决于所讨论的数据类型。对于 QuickCheck,它是从shrink
函数中产生的任何东西。
最好在 QuickCheck 会话中解释:
Prelude Test.QuickCheck> let prop l = all (/= 5) l Prelude Test.QuickCheck> quickCheck 道具 *** 失败的!可证伪(经过 10 次测试和 2 次缩小): [5]
所以这里 QuickCheck 能够给出最小的反例,但是从评论来看,它首先想到了一个更大的列表,然后使用shrink
. 要仔细查看正在发生的事情,我们使用verboseCheck
:
Prelude Test.QuickCheck> verboseCheck 道具 通过: [] 通过: [0] 通过: [-2,1] 通过: [-2,2,-2] 通过: [-4] 失败的: [-1,-2,5,4,2] *** 失败的!通过: [] 失败的: [5,4,2] 通过: [] 通过: [4,2] 失败的: [5,2] 通过: [] 通过: [2] 失败的: [5] 通过: [] 通过: [0] 通过: [3] 通过: [4] 可证伪(经过 6 次测试和 3 次缩小): [5]
QuickCheck 尝试几个命题成立的列表,然后找到[-1,-2,5,4,2]
. 现在它通过尝试它的子列表来减少列表。您可以在 GHCi 中说服自己,shrink [-1,-2,5,4,2] == [[],[5,4,2],[-1,-2,2],...
第二个条目是第一个仍未通过测试的条目。然后 QuickCheck 继续进行并进一步缩小:shrink [5,4,2] == [[],[4,2],[5,2],...
和shrink [5,2]
[[],[2],[5],...
。最后它试图[5]
进一步缩小,但没有一个shrink [5] ==
[[],[0],[3],[4]]
失败的命题,所以最后的计数例子是[5]
。
单一shrink
是逐步降低某些Arbitrary
测试用例的复杂性(“立即缩小”)。这可能类似于2 -> 1
or 1:[] -> []
。对于更复杂的类型,可能有多种方法可以“逐渐缩小”类型,因此您可以在列表中指定所有类型。
例如,树可能会通过移除任何一片叶子来缩小,因此如果有n
叶子,那么缩小会生成一个长度列表n
。