gpt4 book ai didi

testing - 如何在 Haskell 中使用 SmallCheck?

转载 作者:行者123 更新时间:2023-11-28 19:40:51 26 4
gpt4 key购买 nike

我正在尝试使用 SmallCheck测试 Haskell 程序,但我不明白如何使用该库来测试我自己的数据类型。显然,我需要使用 Test.SmallCheck.Series .但是,我发现它的文档非常困惑。我对菜谱式的解决方案和对逻辑(monadic?)结构的可理解解释都感兴趣。以下是我的一些问题(所有相关问题):

  • 如果我有一个数据类型 data Person = SnowWhite | Dwarf Integer ,我该如何解释smallCheck有效值为 Dwarf 1通过Dwarf 7 (或 SnowWhite )?如果我有一个复杂的 FairyTale 怎么办?数据结构和构造函数 makeTale :: [Person] -> FairyTale ,我想要 smallCheck使用构造函数从 Person-s 列表中创建 FairyTale-s?

    我设法做到了 quickCheck通过使用 Control.Monad.liftM 的明智应用,这样不会弄脏我的手像makeTale这样的功能.我想不出用 smallCheck 来做到这一点的方法(请向我解释一下!)。

  • 类型之间有什么关系Serial , Series等等?

  • (可选)coSeries 的意义是什么? ?我如何使用 PositiveSmallCheck.Series 输入?

  • (可选)任何关于在 smallCheck 上下文中什么是单子(monad)表达式背后的逻辑以及什么只是常规函数的说明,我们将不胜感激。

如果有任何使用 smallCheck 的介绍/教程,我会很感激一个链接。非常感谢!

更新:我应该补充说,我为 smallCheck 找到的最有用和最易读的文档是this paper (PDF) .乍一看,我找不到问题的答案;它更像是一个有说服力的广告,而不是一个教程。

更新 2: 我提出了关于奇怪的 Identity 的问题以 Test.SmallCheck.list 的类型显示和其他地方到separate question .

最佳答案

注意:此答案描述的是 SmallCheck 1.0 之前的版本。参见 this blog post了解 SmallCheck 0.6 和 1.0 之间的重要区别。

SmallCheck 与 QuickCheck 相似,因为它测试可能类型空间的某些部分上的属性。不同之处在于,它试图详尽列举一系列所有“小”值,而不是小值的任意子集。

正如我所暗示的,SmallCheck 的 Serial 就像 QuickCheck 的 Arbitrary

现在 Serial 非常简单:Serial 类型 a 有一种方法 (series) 可以生成一个Series 类型,它只是 Depth -> [a] 中的一个函数。或者,要解包,Serial 对象是我们知道如何枚举一些“小”值的对象。我们还获得了一个 Depth 参数,它控制我们应该生成多少小值,但让我们暂时忽略它。

instance Serial Bool where series _ = [False, True]
instance Serial Char where series _ = "abcdefghijklmnopqrstuvwxyz"
instance Serial a => Serial (Maybe a) where
series d = Nothing : map Just (series d)

在这些情况下,我们只是忽略 Depth 参数,然后为每种类型枚举“所有”可能的值。我们甚至可以为某些类型自动执行此操作

instance (Enum a, Bounded a) => Serial a where series _ = [minBound .. maxBound]

这是一种非常简单的彻底测试属性的方法——逐字逐句地测试每一个可能的输入!显然,至少存在两个主要缺陷:(1) 无限数据类型在测试时会导致无限循环,以及 (2) 嵌套类型导致需要查看的示例空间呈指数增长。在这两种情况下,SmallCheck 都会很快变大。

这就是 Depth 参数的要点——它让系统要求我们保持 Series 较小。从文档中,Depth

Maximum depth of generated test values

For data values, it is the depth of nested constructor applications.

For functional values, it is both the depth of nested case analysis and the depth of results.

所以让我们修改我们的例子以保持它们的小。

instance Serial Bool where 
series 0 = []
series 1 = [False]
series _ = [False, True]
instance Serial Char where
series d = take d "abcdefghijklmnopqrstuvwxyz"
instance Serial a => Serial (Maybe a) where
-- we shrink d by one since we're adding Nothing
series d = Nothing : map Just (series (d-1))

instance (Enum a, Bounded a) => Serial a where series d = take d [minBound .. maxBound]

好多了。


那么什么是系列?就像 QuickCheck 的 Arbitrary 类型类中的 coarbitrary 一样,它让我们构建了一系列“小”函数。请注意,我们在输入类型上编写实例——结果类型在另一个 Serial 参数中传递给我们(我在下面调用 results)。

instance Serial Bool where
coseries results d = [\cond -> if cond then r1 else r2 |
r1 <- results d
r2 <- results d]

这些需要更多的独创性来编写,实际上我会推荐你​​使用我将在下面简要描述的 alts 方法。


那么我们如何制作一些 SeriesPerson 呢?这部分很简单

instance Series Person where
series d = SnowWhite : take (d-1) (map Dwarf [1..7])
...

但是我们的 coseries 函数需要生成从 Person 到其他东西的所有可能的函数。这可以使用 SmallCheck 提供的 altsN 系列函数来完成。这是一种写法

 coseries results d = [\person -> 
case person of
SnowWhite -> f 0
Dwarf n -> f n
| f <- alts1 results d ]

基本思想是 altsN resultsN 值生成 SeriesN 元函数Serial 实例到 ResultsSerial 实例。所以我们用它来创建一个函数,从 [0..7],一个先前定义的 Serial 值,到我们需要的任何东西,然后我们将我们的 Person 映射到数字和把它们传进去。


现在我们有了 PersonSerial 实例,我们可以用它来构建更复杂的嵌套 Serial 实例。对于“实例”,如果 FairyTalePerson 的列表,我们可以在我们的旁边使用 Serial a => Serial [a] 实例Serial Person 实例轻松创建Serial FairyTale:

instance Serial FairyTale where
series = map makeFairyTale . series
coseries results = map (makeFairyTale .) . coseries results

((makeFairyTale .)makeFairyTalecoseries 生成的每个函数组合在一起,这有点令人困惑)

关于testing - 如何在 Haskell 中使用 SmallCheck?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16555291/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com