Christoph Schiessl's Blog

Automated Testing with Haskell’s QuickCheck

QuickCheck is one of the libraries, that makes Haskell awesome. Conventionally, testing boils down to writing a number of separate test-cases to express different assertions about a particular piece of code. Testing like that is possible in Haskell (with HUnit), but QuickCheck’s approach is much more subtle…

Instead of individual test-cases, one has to come up with universal properties, the function being tested is thought to satisfy. Universal means, that the function satisfies the property, regardless of the arguments passed to it. Thus, QuickCheck can repeatedly apply the function to randomly generated arguments and check whether the property holds or not.

The test fails, if the property turns out to be falsifiable (i.e. the function violates the proposed property at least once). Otherwise, the test succeeds.


Let me illustrate how this works with a simple example. Suppose, we want to verify that the following function is correct:

1
2
double :: (Integral a) => a -> a
double n = 2 * n

To get started, we need to find an universal property of double. I can think of plenty of suitable properties, but for now, let’s use the following:

double’s return value is always an even number.

Obvious, right? The best thing about it, is that we don’t have to implement it ourselves. Haskell already ships with a predicate function to determine whether a number is even or not. Unsurprisingly, it’s called even:

1
even :: Integral a => a -> Bool

Okay, we now have everything we need to define the property itself:

1
2
prop_doubleEven :: Integer -> Bool
prop_doubleEven n = even (double n)

prop_doubleEven is the function being called by QuickCheck. Its name has to start with prop_ and its return value has be of type Bool. All the arguments are randomly generated by QuickCheck. Finally, we can execute all tests as follows:

1
2
return []
main = $(quickCheckAll)

As expected, QuickCheck informs us that all tests have passed (i.e. prop_doubleEven is not falsifiable):

1
2
=== prop_doubleEven from Double.hs:9 ===
+++ OK, passed 100 tests.

That was easy ;)


Here’s the complete code:

1
2
3
4
5
6
7
8
9
10
11
12
13
{-# LANGUAGE ScopedTypeVariables, TemplateHaskell #-}
module Double where

import Test.QuickCheck.All

double :: Integer -> Integer
double n = 2 * n

prop_doubleEven :: Integer -> Bool
prop_doubleEven n = even (double n)

return []
main = $(quickCheckAll)

In this post, I’ve only outlined the most basic usage of QuickCheck. In reality, there’s a lot more to say about it. But, let’s leave that for another day.

comments powered by Disqus