使用 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 a
和BS.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
的参数上使用 genRSA
和 genRSAMessage
,或者有其他方法吗?
谢谢
您想检查 prop_SignAndVerify key message
所有 许多 key
和 message
。因此,如果我们有一个固定的 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
值。
上下文
我有以下功能:
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 a
和BS.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
的参数上使用 genRSA
和 genRSAMessage
,或者有其他方法吗?
谢谢
您想检查 prop_SignAndVerify key message
所有 许多 key
和 message
。因此,如果我们有一个固定的 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
值。