You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell中Hedgehog库Gen与forAll生成随机输入的差异及结合性测试

嘿,我来帮你把Sum类型的结合性测试补全,再把Gen和forAll的差异讲得明明白白~

1. 完成Sum类型的结合性测试

首先把你没写完的测试代码补全,完整的可运行实现如下:

import Hedgehog
import qualified Hedgehog.Gen as Gen
import Data.Monoid (Sum(..))

-- 生成三个Int的三元组,利用Gen的Applicative实例组合生成器
genTriple :: Gen (Int, Int, Int)
genTriple = liftA3 (,,) Gen.enumBounded Gen.enumBounded Gen.enumBounded

-- 测试Sum类型的结合性:a <> (b <> c) ≡ (a <> b) <> c
prop_assoc :: Property
prop_assoc = property $ do
    (a, b, c) <- forAll genTriple
    let leftAssoc = Sum a <> (Sum b <> Sum c)
        rightAssoc = (Sum a <> Sum b) <> Sum c
    leftAssoc === rightAssoc

几个小细节提醒:

  • 别忘了导入Data.Monoid里的Sum类型,以及Hedgehog的核心模块和Gen子模块
  • 用Hedgehog提供的===操作符做等价性断言,它会在测试失败时自动输出两边的差异
  • 如果你不想测试所有Int值(毕竟范围太大),可以把Gen.enumBounded换成自定义范围的生成器,比如Gen.int (Range.constant (-1000) 1000)来限定数值范围
2. Gen和forAll的核心差异

这俩完全是测试流程里的不同角色,根本不在一个层面上:

  • Gen:数据生成的“蓝图”
    Gen是用来描述如何生成随机测试数据的类型,它本身只是一个“配方”,不会实际生成任何数据。比如你写的genTriple就是一个Gen (Int, Int, Int),它定义了“通过三个枚举所有Int的生成器,组合出三元组”的规则。
    你可以把Gen理解成一个函数的定义——它告诉Hedgehog生成数据的逻辑,但只有被调用时才会干活。

  • forAll:执行蓝图并获取数据
    forAll是Hedgehog测试上下文(PropertyT monad)里的操作,它的作用是拿着你定义好的Gen生成器,实际生成随机测试数据,然后把数据绑定到变量里供测试使用。
    简单说:Gen负责“说清楚要生成什么”,forAll负责“真的生成出来给你用”。

举个更直观的例子:

  • 你写genInt :: Gen Int = Gen.enumBounded,这只是声明了“我要生成所有可能的Int”,但此时没有任何Int被创建。
  • 当你在property $ do块里写x <- forAll genInt,Hedgehog才会按照genInt的规则生成一个随机Int,把它绑定给x,你接下来就能用x编写测试逻辑了。

另外,forAll还有个超实用的特性:如果测试失败,它会自动**缩小(shrink)**生成的数据,帮你找到最小的能触发失败的输入——而这个缩小策略是和Gen绑定的,每个Gen都自带对应的缩小逻辑(比如Gen.enumBounded的缩小会逐步往0靠近)。

内容的提问来源于stack exchange,提问作者Shersh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:53:41