使用 QuickCheck 为给定函数生成多个任意参数

Using QuickCheck to generate multiple arbitrary parameters for a given function

上下文

我有以下功能:

prop_SignAndVerify :: (PrivKey a b) => Blind a -> BS.ByteString -> Bool
prop_SignAndVerify bsk msg = case verify pk msg sig of
                             Left e -> error e
                             Right b -> b
  where
    sk  = getBlind bsk
    pk  = toPublic sk
    sig = case sign sk msg of
               Left e -> error e
               Right s -> s

我想做这样的事情:

-- instance PrivKey RSA.PrivateKey RSA.PublicKey where...
genRSA :: Gen RSA.PrivateKey
genRSAMessage :: Gen BS.ByteString

main = do
  quickCheck . verbose 
  $ forAll genRSA 
  $ forAll genRSAMessage prop_SignAndVerify

也就是说,我想使用显式生成器为prop_SignAndVerify的参数中的Blind aBS.ByteString生成任意值。

但是,上面的代码不起作用,因为函数 forAll 具有类型签名:

forAll :: (Show a, Testable prop) => Gen a -> (a -> prop) -> Property

此函数运行生成器并将生成的任意值应用到 (a -> prop),返回 Property。然而,这个 Property 不能进一步部分应用;它隐藏了底层功能。

我认为上述工作需要的是:

forAll' :: (Show a,  Testable prop) => Gen a -> (a -> prop) -> prop

问题

所以我的问题是,如何在 prop_SignAndVerify 的参数上使用 genRSAgenRSAMessage,或者有其他方法吗?

谢谢

您想检查 prop_SignAndVerify key message 所有 许多 keymessage。因此,如果我们有一个固定的 key,我们的测试将如下所示:

main = do
  quickCheck . verbose $ 
    let key = someGeneratedKey
    in forAll genRSAMessage $ \message ->
         prop_SignAndVerify key message

如果我们有固定的 message,我们的测试将如下所示:

main = do
  quickCheck . verbose $ 
    forAll genRSAMessage $ \key ->
      let message = someMessage
      in prop_SignAndVerify key message

我们所要做的就是结合两种变体:

main = do
  quickCheck . verbose $ 
    forAll genRSA        $ \key ->
    forAll genRSAMessage $ \message ->
      prop_SignAndVerify key message

由于 eta 转换,您可以去掉 message,但我认为测试应该易于阅读。

您可以利用 Gen 的单子性质从您的 属性:

组成一个更复杂的 Gen
main =
  let g = do
            key <- genRSA
            message <- genRSAMessage
            return $ prop_SignAndVerify (Blind key) message
  in quickCheck . verbose $ forAll g id

这里,g是一个Gen Bool值。

或者,您可以利用 Gen 的应用性质,并使用 <*>:

编写 g
main =
  let g =
        return (\key message -> prop_SignAndVerify (Blind key) message)
        <*> genRSA
        <*> genRSAMessage
  in quickCheck . verbose $ forAll g id

g 在这里仍然是一个 Gen Bool 值。