

"/info" === defaultQuery{flags = [Flag "info" ""]}

"/count=10" === defaultQuery{flags = [Flag "count" "10"]}



do

(===)

(>>)



data Test a = Pass



instance Monad Test where

a >> b = a `seq` b



instance Show (Test a) where

show x = x `seq` "All tests passed"



pass :: Test ()

pass = Pass



Test

Pass

error

pass





parseTest f input output =

case f input of

Left x -> err "Parse failed" (show x)

Right x -> if x == output then pass else

err "Parse not equal" (show x)

where

err pre post = error $ pre ++ ":

" ++

input ++ "

" ++

show output ++ "

" ++

post



parseTest

pass



parse_Query = do

let (===) = parseTest parseQuery



(===)

show

Lots of people have written clever things about writing monads - their mathematical structure, properties etc. I wrote a Test Monad for my FilePath library, which I've since refined for Hoogle. The inspiration for this comes from Lennart Augustsson , who gave a talk at fun in the afternoon on ways to abuse Haskell syntax for other purposes.For the testing of the parser in Hoogle, I want to write a list of strings, versus the data structures they should produce:In Haskell, the most syntactically pleasant way of writing a list of things is usingnotation in a monad. This means that eachoperation should be in a monad, and thebind operation should execute one test, then the next. We also need some way of "executing" all these tests. Fortunately this is all quite easy:The basic type is, which has only one value, being. To represent failure, simply call, which is failure in all Haskell programs and allows you to give a useful error message. The helper functionis provided to pin down the type, so you don't get ambiguity errors. The Monad instance simply ensures that all the tests are evaluated, so that if any crash then the whole program will crash. The Show instance demands that all the tests passed, then gives a message stating that.We can then layer our own test combinators on top, for example for parsec:Thisfunction takes a parsec parser, an input and an output. If the parse fails, or doesn't produce the correct answer, an error is raised. If the test passes, then the function calls. It's then simple enough to define each test set as:Hereis defined differently for each do block. By evaluating one of these test blocks in an interpreter, themethod will automatically be called, executing all the tests.I've found this "monad" lets you express tests very succinctly, and execute them easily. I moved to this test format from stand-alone files listing sample inputs and expected results, and its proved much easier to maintain and more useful.