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.
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to