为什么Haskell QuickCheck自定义Arbitrary实例的shrink未被调用?
问题原因
你遇到的shrink未调用、无收缩操作的核心原因是前置条件过滤了所有收缩候选:
- 你定义的属性前置条件为
length s > 5,即要求测试字符串长度至少为6 - 触发失败的反例长度恰好为6,而你实现的
shrink逻辑是每次删除一个字符,所有收缩生成的字符串长度都为5,完全不满足前置条件 - QuickCheck在收缩流程中会自动丢弃所有不满足
==>前置条件的候选,当没有任何有效收缩候选可用时,就会直接终止收缩流程,最终表现为numShrinks和numShrinkTries均为0
验证方法
你可以做任意一个修改验证该结论:
- 把前置条件调整为
length s >= 5,收缩后长度为5的字符串可以通过前置检查,就能触发正常的收缩流程,你添加的trace日志也会正常输出 - 把长度限制逻辑移到生成器内部,同时调整shrink逻辑保证收缩后的结果仍满足长度要求,避免无效收缩候选被过滤,示例如下:
-- 生成器保证输出长度>=6的字符串 simpleString = sized $ \n -> do len <- choose (6, max 6 n) replicateM len $ choose ('a','d') -- shrink逻辑保证收缩后长度仍>=6 shrink (SimpleString ss) = [SimpleString ss' | ss' <- removeOne ss, length ss' >= 6]
内容的提问来源于stack exchange,提问作者Ernesto Posse
相关产品推荐
相关产品推荐

