41

我正在学习 QuickCheck >= 2.6 的技巧,但我不明白收缩是什么。从类型签名来看,收缩看起来更像是扩展!请照亮我:)

4

2 回答 2

67

当 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]

于 2013-06-06T18:57:32.557 回答
10

单一shrink是逐步降低某些Arbitrary测试用例的复杂性(“立即缩小”)。这可能类似于2 -> 1or 1:[] -> []。对于更复杂的类型,可能有多种方法可以“逐渐缩小”类型,因此您可以在列表中指定所有类型。

例如,树可能会通过移除任何一片叶子来缩小,因此如果有n叶子,那么缩小会生成一个长度列表n

于 2013-06-06T17:36:59.957 回答