使用带有QuickCheck / monadicIO的Reader monad

时间:2014-07-30 20:12:48

标签: haskell quickcheck reader-monad

我想将一个整数作为CLI参数传递给使用QuickCheck / monadicIO的Haskell程序。该整数将在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

1 个答案:

答案 0 :(得分:1)

maxSegmentLengthCeiling参数设为propHasExpectedLengthCeiling

propHasExpectedLengthCeiling :: Int -> CustomInput -> Property

并将其作为

调用
main = do 
  [n] <- getArgs
  quickCheckWith customArgs (propHasExpectedLengthCeiling (read n))