Note, QuickCheck doesn't need to just be an embedded domain specific language for testing ''Haskell'' code. By making instances of Arbitrary for FFI types you can use Haskell and QuickCheck to check code in other languages.

Contents

-- I've written a function that looks similar to this one
getList = find 5where
find 0=return[]
find n =do
ch <-getCharif ch `elem` ['a'..'e']thendo
tl <- find (n-1)return(ch : tl)else
find n
-- I want to test this function, without hitting the filesystem. In C++ I-- would use a istringstream. I couldn't find a function that returns a-- Handle from a String. The closer thing that may work that I could find-- was making a pipe and converting the file descriptor. Can I simplify-- that function to take it out of the IO monad?

So the problem is: how to effectively test this function in Haskell? The
solution we turn to is refactoring and QuickCheck.

The reason your getList is hard to test, is that the side effecting monadic code
is mixed in with the pure computation, making it difficult to test
without moving entirely into a "black box" IO-based testing model.
Such a mixture is not good for reasoning about code.

Let's untangle that, and then test the referentially transparent
parts simply with QuickCheck. We can take advantage of lazy IO firstly,
to avoid all the unpleasant low-level IO handling.

So the first step is to factor out the IO part of the function into a
thin "skin" layer:

Now we can test the 'guts' of the algorithm, the take5 function, in
isolation. Let's use QuickCheck. First we need an Arbitrary instance for
the Char type -- this takes care of generating random Chars for us to
test with. I'll restrict it to a range of nice chars just for
simplicity:

Let's fire up GHCi (or Hugs) and try some generic properties (its nice
that we can use the QuickCheck testing framework directly from the
Haskell prompt). An easy one first, a [Char] is equal to itself:

*A> quickCheck ((\s -> s == s)::[Char]->Bool)
OK, passed 100 tests.

What just happened? QuickCheck generated 100 random [Char] values, and
applied our property, checking the result was True for all cases.
QuickCheck generated the test sets for us!

One issue with the default QuickCheck configuration, when testing
[Char], is that the standard 100 tests isn't enough for our situation.
In fact, QuickCheck never generates a String greater than 5 characters
long, when using the supplied Arbitrary instance for Char! We can confirm
this:

*A> quickCheck (\s ->length(take5 s)<5)
OK, passed 100 tests.

QuickCheck wastes its time generating different Chars, when what we
really need is longer strings. One solution to this is to modify
QuickCheck's default configuration to test deeper:

deepCheck p = check (defaultConfig { configMaxTest =10000}) p

This instructs the system to find at least 10000 test cases before
concluding that all is well. Let's check that it is generating longer
strings:

QuickCheck is effectively an embedded domain specific language for
testing Haskell code, and allows for much more complex properties than
those you've seen here to be tested. Some sources for further reading
are:

Note, QuickCheck doesn't need to just be an embedded domain specific language for testing Haskell code. By making instances of Arbitrary for FFI types you can use Haskell and QuickCheck to check code in other languages.