将 Reader monad 与 QuickCheck / monadicIO 结合使用
-
21-12-2019 - |
题
我想将一个整数作为 CLI 参数传递给使用 QuickCheck / 的 Haskell 程序 monadicIO
. 。该整数将在 assert
使测试可定制。问题是,一旦我解析了整数值 main
, ,我不知道如何将它传递到内部 monadicIO
调用而不使用像 IORef
. 。我认为一个优雅的解决方案可能是 Reader
monad,但我找不到让它工作的解决方案,被视为 quickCheck
其论据是僵化的。有任何想法吗?
后来编辑1: 根据要求,我附上了我正在尝试但失败的实际代码。注释掉的行代表我失败的尝试。背景:该测试套件旨在练习一个非常简单的远程端点,该端点计算 QuickCheck 生成的随机输入的 SHA512。远程端点基于 Python/Flask。
稍后编辑2响应@user2407038: 我可以做 propHasExpectedLengthCeiling
接受一个 Int 类型的附加参数,但是 quickCheck
会为其生成随机值,但这不是我想要发生的事情。我的目标是使用 maxSegmentLengthCeiling
我从命令行参数中获取并使用它 let testPassed = actualMaxSegmentLength <= maxSegmentLengthCeiling
里面的 monadicIO
堵塞。现在 maxSegmentLengthCeiling
被指定为顶级值,这意味着每次更改该值时都必须重新编译代码。我还没有任何涉及的代码 IORef
因为这是最后的手段,我的问题的本质是如何避免 IORef
路线。
import qualified Data.ByteString.Lazy.Char8 as LC
import Control.Applicative ( (<$>) )
import Data.Function ( on )
import Data.List ( groupBy )
import Data.Char ( isDigit )
--import Safe ( headMay
-- , readMay
-- )
--import System.Environment ( getArgs )
import Network.HTTP.Conduit ( simpleHttp )
import Test.QuickCheck ( Arbitrary
, Property
, arbitrary
, choose
, frequency
, quickCheckWith
, stdArgs
, vectorOf
)
import Test.QuickCheck.Test ( Args
, maxSuccess
)
import Test.QuickCheck.Monadic ( assert
, monadicIO
, run
)
newtype CustomInput = MkCustomInput String deriving Show
instance Arbitrary CustomInput where
arbitrary =
let
genCustomInput = vectorOf 20
$ frequency [ (26, choose ('0','9'))
, (10, choose ('a','z'))
]
in
MkCustomInput <$> genCustomInput
maxSegmentLengthCeiling :: Int
maxSegmentLengthCeiling = 22
urlPrefix :: String
urlPrefix = "http://192.168.2.3:5000/sha512sum/"
propHasExpectedLengthCeiling :: CustomInput -> Property
propHasExpectedLengthCeiling (MkCustomInput input) = monadicIO $ do
testPassed <- run $ do
response <- simpleHttp $ urlPrefix ++ input
let stringResponse = LC.unpack response
let brokenDownStringResponse = groupBy ( (==) `on` isDigit ) stringResponse
let actualMaxSegmentLength = maximum $ map length brokenDownStringResponse
let testPassed = actualMaxSegmentLength <= maxSegmentLengthCeiling
putStrLn ""
putStrLn ""
putStrLn $ "Input: " ++ input
putStrLn $ "Control sum: " ++ stringResponse
putStrLn $ "Breakdown: " ++ show brokenDownStringResponse
putStrLn $ "Max. length: " ++ show actualMaxSegmentLength
putStrLn $ "Ceiling: " ++ show maxSegmentLengthCeiling
putStrLn $ "Test result: " ++ if testPassed then "Pass" else "Fail"
putStrLn ""
putStrLn ""
return testPassed
assert $ testPassed
customArgs :: Args
customArgs = stdArgs { maxSuccess = 1000000 }
--readMayAsInt :: String -> Maybe Int
--readMayAsInt = readMay
main :: IO ()
main =
--main = do
-- cliArgs <- getArgs
-- let ceilingInputMay = headMay cliArgs >>= readMayAsInt
-- maxSegmentLengthCeiling <- case ceilingInputMay of
-- (Just lengthCeiling) -> return lengthCeiling
-- Nothing -> error "No valid number given"
quickCheckWith
customArgs
propHasExpectedLengthCeiling
解决方案
制作 maxSegmentLengthCeiling
一个参数 propHasExpectedLengthCeiling
:
propHasExpectedLengthCeiling :: Int -> CustomInput -> Property
并将其调用为
main = do
[n] <- getArgs
quickCheckWith customArgs (propHasExpectedLengthCeiling (read n))