【发布时间】:2018-08-26 21:07:35
【问题描述】:
我想根据某种“上下文”生成随机术语,我想知道这是否可以使用快速检查。基本上我想传递一个额外的数据类型,以便任意函数可以根据附加参数生成术语......这可以通过快速检查实现还是我应该只编写自己的 Gen 定义?
【问题讨论】:
标签: haskell quickcheck
我想根据某种“上下文”生成随机术语,我想知道这是否可以使用快速检查。基本上我想传递一个额外的数据类型,以便任意函数可以根据附加参数生成术语......这可以通过快速检查实现还是我应该只编写自己的 Gen 定义?
【问题讨论】:
标签: haskell quickcheck
虽然 Daniel Wagner 为 QuickCheck (+1) 提供了一个很好的答案,但它也突出了 QuickCheck 的一个弱点。在 QuickCheck 中,使用 Arbitrary 的实例编写属性,但由于其设计,Arbitrary 不是一元的。
Daniel Wagner 分享的解决方法是 Gen,另一方面,是 monadic,因此您可以使用 do 表示法编写上下文相关的代码。缺点是虽然您可以将 Gen a 转换为 Arbitrary a,但您要么必须提供自定义的 shrink 实现,要么放弃收缩。
另一个用于基于属性的测试的库 Hedgehog 的设计方式是属性本身是一元的,这意味着您可以编写整个属性并简单地嵌入特定于上下文的特定值测试代码本身的生成(包括收缩):
propWithContext :: Property
propWithContext = property $ do
ctx <- forAll genContext
n <- forAll $ Gen.integral $ Range.linear 0 $ chooseIntRange ctx
let ctx' = updateContext n ctx
-- Exercise SUT and verify result here...
这里,genContext 是Context 类型的自定义生成器,类型为
genContext :: MonadGen m => m Context
【讨论】:
在arbitrary 中执行此操作是可能的,尽管不是很理智。但是如果你退出arbitrary,你可以字面上只传递一个额外的参数。
-- do whatever you want inside the implementation of these two
chooseIntRange :: Context -> Int
updateContext :: Int -> Context -> Context
arbitraryIntWithContext :: Context -> Gen (Context, Int)
arbitraryIntWithContext ctx = do
n <- choose (0, chooseIntRange ctx)
return (n, updateContext n ctx)
使用StateT 可以稍微缓解上下文的管道问题,例如
-- do whatever you want inside the implementation of this
chooseIntRangeAndUpdate :: MonadState Context m => m Int
arbitraryIntStateT :: StateT Context Gen Int
arbitraryIntStateT = do
hi <- chooseIntRangeAndUpdate
lift (choose (0, hi))
【讨论】: