Submitted by Moonside in programming

So I finally decided to learn some kind of a testing framework or library for Haskell. I chose the widely imitated QuickCheck - over 30 libraries in different languages have sprung up to imitate its basic concept. In short, it lets you specify laws that your functions ought to obey. QuickCheck then hammers the functions with random inputs and reports if they've been naughty. This as opposed to unit testing, where you check that functions against single input values - yes, indeed the square root of 4 is 2 and so on. Thus QuickCheck-like system have the promise of testing more with less effort, although it's not a replacement for unit testing.

There are is no end to tutorials about the small scale details, writing the laws, or propositions as they known in the library and writing the generators that create random data for testing. Thus I could write a test like the following quickly enough:

prop_Associativity :: Interval -> Interval -> Interval -> Bool
prop_Associativity x y z =
    x <> (y <> z) == (x <> y) <> z

which specifies that the diamond operator <>, also known as mappend, must be associative or else. With a little more fighting I figured out how to write generators too and could then expand the test coverage to the core part of the program and wrote almost 30 of them.

But there's a lack of information on how to actually make QuickCheck tests run sensibly. I wanted to make to make a test module for each module of my program and then run the tests centrally and then run them using the cabal test command which supposedly integrates testing with the building and packaging system. Neither the QuickCheck nor the Cabal manuals offered help, but I got enough hints obliquely from some random's The Design and Use of QuickCheck blogpost to make it work after like third hour of research. There was a tutorial for Stack, which is another building/packaging system but I ain't using that.

This is utterly bollocks imo. Now that I know how to actually do what I wanted to, I could suddenly find more sources just by Googling, but that's too little too late.

Also, unrelatedly, I discovered some crashing bug ('panic') in the compiler, GHC, that apparently ought to be reported but I can't be arsed to since I did something to make it go away. I beat the compiler into submission and reign over it on my bone throne and it's all I care about.

4

Comments

You must log in or register to comment.

musou wrote

yeesh that sounds like no fun at all. i tried using the erlang version of that quickcheck library for property based testing in elixir and also found it hard to use effectively. the docs weren't great and i couldn't always figure out how to make the generators specific enough. i just stick with type annotations and unit tests and that's usually good enough for the kind of thing i do.

for what little its worth ive had an easier time using stack with haskell instead of cabal, but only because the book i was learning from used it. but googling for solutions to haskell problems has gotten more difficult now that there's two competing toolchains

1

Moonside OP wrote

I can't avoid making up a conspiracy theory that QuickCheck library presently exists as an advert for QuviQ AB and this perhaps goes even further for the Erlang version. Good enough to become somewhat well-known and whet your appetite and then when you get serious, you need to bring consults in.

I was only testing simple mathematical properties (semigroup/monoid/group/Abelian group laws and involutivity) where generator specificity doesn't matter that much. The properties are so general that if a fault was discovered, it would taint a function for all inputs, even accidentally correct.

The one upshot of the whole shebang was that I got to reimplement some stuff as groups and I could just feel the power rushing through my veins ad hoc 'negation' functions into invert ones. Actually now I looked more into the docs and noticed that Haskell also has Abelian groups as a type class and I added them too to my types.

(It would be really nice if there was something that could automatize writing tests for common typeclasses. I have two types that have instances of semigroups, monoids, groups and Abelian groups and doing tests like I did means that testing their properties and a direct consequence of one took 14 tests which is kind of gross when you could write generic code.)

for what little its worth ive had an easier time using stack with haskell instead of cabal, but only because the book i was learning from used it. but googling for solutions to haskell problems has gotten more difficult now that there's two competing toolchains

I tried out Stack because there's some Emacs tooling around that, but the tooling was quite brittle which soured me a bit on that. There seems to be some amount of Stack related drama in the Haskell community and some people I've learned from have a strong distaste for Stack in favor of Cabal. I'm not in a place to know enough to switch and the problem wasn't really Cabal related. Rather it was, in the small, that the solution involved Template Haskell and sequencing IO actions both of which I haven't done before and, in the large, that no-one has bothered to write documentation or a tutorial on the bird's eye view of things. Or what a best practices could be, I'm not Haskell literate to just see what popular packages are doing.

2