Using QuickCheck to generate multiple arbitrary parameters for a given function

孤街浪徒 提交于 2021-01-27 11:52:40

问题


Context

I have the following 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

I would like to do something like:

-- 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

That is, I would like to use explicit generators to generate arbitrary values for Blind a and BS.ByteString in the parameters of prop_SignAndVerify.

The code above, however, doesn't work, because the function forAll has type signature:

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

This function runs the generator and apples the generated arbitrary value to (a -> prop), returning a Property. This Property however cannot be further partially applied; it hides the underlying function.

I think what we need for the above to work would be something like:

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

Question

So my question is, how can I use genRSA and genRSAMessage over the parameters of prop_SignAndVerify, or is there an alternative approach?

Thanks


回答1:


You could take advantage of the monadic nature of Gen to compose a more complex Gen value from your property:

main =
  let g = do
            key <- genRSA
            message <- genRSAMessage
            return $ prop_SignAndVerify (Blind key) message
  in quickCheck . verbose $ forAll g id

Here, g is a Gen Bool value.

Alternatively, you can take advantage of the applicative nature of Gen and compose g using <*>:

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

g is still a Gen Bool value here as well.




回答2:


You want to check prop_SignAndVerify key message for all many keys and messages. So if we had a fixed key, our tests would look like that:

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

If we had a fixed message, our test would look like this:

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

All we have to do is to combine both variants:

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

You can get rid of the message due to eta conversion, but in my opinion tests should be easily readable.



来源:https://stackoverflow.com/questions/45519955/using-quickcheck-to-generate-multiple-arbitrary-parameters-for-a-given-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!