繁体   English   中英

如何使用QuickCheck测试高阶函数?

[英]How can I test a higher-order function using QuickCheck?

我有一个我想要测试的高阶函数,我要测试的一个属性就是传入的函数所做的。为了说明的目的,这是一个人为的例子:

gen :: a -> ([a] -> [a]) -> ([a] -> Bool) -> a

这个想法大致是这是一个示例生成器。 我会用一个启动a ,创建一个单列表[a]然后作出新的名单[a]直到谓词告诉我停下来。 呼叫可能如下所示:

gen init next stop

哪里

init :: a
next :: [a] -> [a]
stop :: [a] -> Bool

这是我要测试的属性:

在任何对gen init next stop调用中, gen承诺永远不会将空列表传递给next

我可以使用QuickCheck测试此属性吗?如果是,如何测试

虽然如果你给gen的实现会有所帮助,我猜它是这样的:

gen :: a -> ([a] -> [a]) -> ([a] -> Bool) -> a
gen init next stop = loop [init]
  where
    loop xs | stop xs   = head xs
            | otherwise = loop (next xs)

您要测试的属性是next永远不会提供空列表。 测试这个问题的一个障碍是你要检查gen内部循环不变量,因此需要从外部获取。 让我们修改gen以返回此信息:

genWitness :: a -> ([a] -> [a]) -> ([a] -> Bool) -> (a,[[a]])
genWitness init next stop = loop [init]
  where
    loop xs | stop xs   = (head xs,[xs])
            | otherwise = second (xs:) (loop (next xs))

我们使用Control.Arrow中的 second 原始的gen很容易根据genWitness:定义genWitness:

gen' :: a -> ([a] -> [a]) -> ([a] -> Bool) -> a
gen' init next stop = fst (genWitness init next stop)

由于懒惰的评估,这不会给我们带来太多的开销。 回到酒店! 要启用从QuickCheck显示生成的函数,我们使用模块Test.QuickCheck.Function 虽然这里并不是绝对必要的,但一个好习惯是单一属性:我们使用Int的列表而不是允许单态限制使它们成为单元列表。 现在让我们说明这个属性:

prop_gen :: Int -> (Fun [Int] [Int]) -> (Fun [Int] Bool) -> Bool
prop_gen init (Fun _ next) (Fun _ stop) =
    let trace = snd (genWitness init next stop)
    in  all (not . null) trace

让我们尝试使用QuickCheck运行它:

ghci> quickCheck prop_gen

有些事情似乎环......是当然的: gen循环,如果stop在列出了从next从来都不是True 让我们改为尝试查看输入轨迹的有限前缀:

prop_gen_prefix :: Int -> (Fun [Int] [Int]) -> (Fun [Int] Bool) -> Int -> Bool
prop_gen_prefix init (Fun _ next) (Fun _ stop) prefix_length =
    let trace = snd (genWitness init next stop)
    in  all (not . null) (take prefix_length trace)

我们现在很快得到一个反例:

385
{_->[]}
{_->False}
2

第二个函数是next参数,如果它返回空列表,那么gen的循环将给next一个空列表。

我希望这能回答这个问题,并且它为您提供了如何使用QuickCheck测试高阶函数的一些见解。

滥用它可能是不好的尝试,但如果QuickCheck抛出异常,它确实会失败。 因此,要测试,只需给它一个函数,为空案例抛出异常。 适应danr的答案:

import Test.QuickCheck
import Test.QuickCheck.Function
import Control.DeepSeq

prop_gen :: Int -> (Fun [Int] [Int]) -> (Fun [Int] Bool) -> Bool
prop_gen x (Fun _ next) (Fun _ stop) = gen x next' stop `deepseq` True
  where next' [] = undefined
        next' xs = next xs

此技术不需要您修改gen

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM