是否可以在QuickCheck中生成任意函数

时间:2018-05-31 17:43:15

标签: haskell quickcheck

我正在尝试为身份编写QuickCheck测试

f $ y = f y

我最初的计划是编写一个返回函数的任意生成器&整数,具有签名Gen (Int -> Int, Int)

并且在prop_DollerDoesNothing测试中,带/不带$的函数应用程序会得到相同的结果。

这是我的代码:

  prop_DollarDoesNothing :: Property
  prop_DollarDoesNothing =
    forAll arbitraryFuncInt (\(f, y) -> (f $ y) == (f y))

  arbitraryFuncInt :: Gen (Int -> Int, Int)
  arbitraryFuncInt = do
    f <- elements [(\x -> x*2), (\x -> x+3), (\x -> x-2)]
    y <- arbitrary :: Gen Int
    return (f, y)

它生成了以下有用的错误消息:

    * No instance for (Show (Int -> Int))
        arising from a use of `forAll'
        (maybe you haven't applied a function to enough arguments?)
    * In the expression:
        forAll arbitraryFuncInt (\ (f, y) -> (f $ y) == (f y))
      In an equation for `prop_DollarDoesNothing':
          prop_DollarDoesNothing
            = forAll arbitraryFuncInt (\ (f, y) -> (f $ y) == (f y))

所以,我修复了错误并通过应用任意函数并从arbitraryFuncInt

返回一对int来使测试正常工作
  prop_DollarDoesNothing :: Property
  prop_DollarDoesNothing =
    forAll arbitraryFuncInt (\(x, y) -> x == y)

  arbitraryFuncInt :: Gen (Int, Int)
  arbitraryFuncInt = do
    f <- elements [(\x -> x*2), (\x -> x+3), (\x -> x-2)]
    y <- arbitrary :: Gen Int
    return (f $ y, f y)

我的问题是:

  1. 由于没有Show的实例,根本无法返回未完全应用的任意函数吗?
  2. 我可以为Show (Int -> Int)编写一个实例以使# 1成为可能吗?
  3. QuickCheck可以在给定类型签名的情况下生成任意函数,以便我可以测试对于所有函数(给定类型)都是真的标识。上面,我手动指定了3个测试函数,我想以某种方式自动化,理想情况下这样f <- arbitrary :: Gen (Int -> Int)

2 个答案:

答案 0 :(得分:3)

QuickCheck支持使用Fun类型生成,缩小和显示函数。 CoArbitrary可以生成函数。然后将其转换为(可能是无限的)类似于trie的结构,可以检查并缩小到有限值(因为测试失败仅取决于有限的许多输入),然后可以将其显示为反例。

具体来说,您可以将属性写为带有Fun参数的函数,该参数是(->)的包装器,使用我描述的机制。用Fn模式解构它以获得一个函数。

prop_dollarDoesNothing :: Property
prop_dollarDoesNothing = property $ \(Fn (f :: Int -> Int)) x ->
  (f $ x) === f x

了解更多信息

答案 1 :(得分:1)

Arbitrary可以很好地生成函数(假设参数是CoArbitrary的实例),它只是显示不起作用的部分。显示功能并不是一个好方法。

这是一个常见问题,因此QuickCheck提供了Blind修饰符。它基本上伪造了任何类型的Show个实例,而不实际显示有关该值的任何信息。当然,这在某种程度上会降低失败的测试用例的调试实用性,但是对于这一点并没有太大的帮助。