[Haskell-cafe] TDD in Haskell
Neil Brown
nccb2 at kent.ac.uk
Tue May 25 08:54:25 EDT 2010
On 25/05/10 12:36, Ionut G. Stan wrote:
> Hi,
>
> I'm doing TDD in pretty much all of the languages that I know, and I
> want to introduce it early in my Haskell learning process. I wonder
> though, if there's some established process regarding TDD, not unit
> testing.
>
> I've heard of QuickCheck and HUnit, but I've also read that QuickCheck
> is used mostly for testing pure code, while HUnit for impure code?
I tend to use HUnit more than QuickCheck (even for pure code), but that
may be a reflection of the projects I've worked on. Reasons why I have
used HUnit:
- Writing an Arbitrary instance for a data type wasn't worth the
effort. When writing a nanopass compiler, it seemed like too much
effort to write an Arbitrary instance to produce a valid fragment of AST
for that particular pass (given that each pass would probably need a
different instance). Problems with the instance include:
* If I allowed every instance to generate the full variety of AST
elements, the chances of generating an AST fragment that tested the pass
in question would be vanishingly small, which would require customising
the instance (or adding generation conditions) in fiddly ways for each
pass to make sure I got good coverage.
* For most passes, the AST must obey many pre-conditions, such as all
variables being declared before use, all expressions type-checking,
passing various safety/sanity checks and so on. All these (and
different sets of them) would need to be considered for each Arbitrary
instance, which adds a lot of effort to the instance generation, when I
can easily craft a few targeted pieces of input myself.
- With some functions I can provide an exhaustive check of all inputs,
for which QuickCheck is ill-suited. This would suit SmallCheck and
LazySmallCheck, but often it's just easier to write something like:
assertTrue $ and [checkResult x (f x) | x <- allInputs] than to use the
other frameworks.
- For QuickCheck 1, testing of things like IO actions was very difficult
(I think unsafePerformIO was needed?) but QuickCheck 2 is much better in
that regard once you've gone the hang of it.
More generally, I often think it is less effort to test the corner/edge
cases carefully with HUnit than it is to express the properties of the
function (and the properties are almost always noticeably longer than
the function being tested) and construct an Arbitrary instance that
produces valid input. Perhaps I just haven't worked on the right
projects for QuickCheck or haven't got into the property-based testing
mindset, though.
Thanks,
Neil.
More information about the Haskell-Cafe
mailing list