The Test.QuickCheck.Monadic module allows you to test monadic code, even those that work in IO .
The monadic property test is of type PropertyM ma , where m is the monad in which the test passes and a is ultimately ignored. In the case of PropertyM IO a you convert the monadic test to Property using monadicIO ; for all other monads, you use monadic instead (which takes a function to run the monad, something IO doesn't have).
In a monadic test, the value of return ed from the monad is ignored. To test the expression, use assert ; assert false value will complete the test. Use run to execute code in the monad being checked.
Other monadic actions are at your disposal. For example, pick will generate new test inputs from Gen a , and pre will check the test prerequisites. They are useful if the test inputs or preconditions themselves depend on the values โโcalculated through the monad being tested, in which case the normal way of generating the inputs or checking the preconditions will not work.
Here is an example of testing some IO code: we verify that after writing something to a temporary file, we can read the same data back. For demonstration purposes, we will impose a prerequisite so that we write at least one byte to a file. Two test properties do the same; one uses pick and pre unnecessarily, and the other does not.
import System.Directory (removeFile) import System.IO (hGetContents, hPutStr, hSeek, openBinaryTempFile, SeekMode (..)) import Test.QuickCheck (arbitrary, Property, quickCheck, (==>)) import Test.QuickCheck.Monadic (assert, monadicIO, pick, pre, run) -- Demonstrating pick and pre as well: prop_writeThenRead :: Property prop_writeThenRead = monadicIO $ do writtenData <- pick arbitrary pre $ not (null writtenData) readData <- run $ writeThenRead writtenData assert $ writtenData == readData -- A more idiomatic way to write the above: prop_writeThenRead2 :: [Char] -> Property prop_writeThenRead2 writtenData = not (null writtenData) ==> monadicIO test where test = do readData <- run $ writeThenRead writtenData assert $ writtenData == readData writeThenRead :: [Char] -> IO [Char] writeThenRead output = do (path, h) <- openBinaryTempFile "/tmp" "quickcheck.tmp" removeFile path hPutStr h output hSeek h AbsoluteSeek 0 hGetContents h main :: IO () main = do quickCheck prop_writeThenRead quickCheck prop_writeThenRead2
Paul Kuliniewicz May 31 '10 at 11:45 a.m. 2010-05-31 23:45
source share