Re: [Haskell-cafe] Type System vs Test Driven Development

Evan Laforge
QuickCheck seems to fit well when you have small input and output spaces, but complicated stuff in the middle, but still simple relations between the input and output. I think that's why data structures are so easy to QuickCheck. I suppose I should look around for more use of QuickCheck for non-data structures... the examples I've seen have been trivial stuff like 'reverse . reverse = id'.
I second this feeling... For example, I've never seen (I've not looked hard, though) Quickcheck's testing applied on graphs. Generating "interesting" (whatever that means for your particular problem) graphs doesn't seem to be a trivial test, even if it's a mere data structure... Does anyone know of such examples? Maybe genetic programming producing graphs is of relevance here (where generating candidate graphs have exactly the same problem). Also, I wonder how you guys do when you're trying to tests code using a lot of numbers (be them floating point or even integer). E.g., integers: A code doing addition and substraction of some sort. A property such as "X = (X add Y) sub Y" is easily falsifiable when the number of bits of your integer is too small for your numbers. E.g. floating point numbers: A code doing coordinate transformations of some sort. Properties akin to "roundtripping" can be easily formulated (e.g. "X = trfY_toX(trfX_toY(X))"), but they'll be falsified often due to lost bits in the mantissa. Replacing strict equality (almost always meaningless for floats) with approximate equality will work. However, definitions of such an approximate equality might vary (the immediate idea is to compare the difference of the two numbers with a small "epsilon", say 10^-9, but that will obviously not work if you numbers are around 10^-12...). And there are also, of course, overflows and underflows, and singularities (e.g. division by zero), non invertibility... So, in addition to defining the approximation (not always easy as I tried to "demonstrate" above) to be used in comparisons, one probably needs ad'hoc generators whose complexity might very well exceed that of the code one wants to test... So, do you have any "methodology" for such use cases? --Serge

On 12 January 2011 21:16, Serge Le Huitouze
Evan Laforge
wrote: QuickCheck seems to fit well when you have small input and output spaces, but complicated stuff in the middle, but still simple relations between the input and output. I think that's why data structures are so easy to QuickCheck. I suppose I should look around for more use of QuickCheck for non-data structures... the examples I've seen have been trivial stuff like 'reverse . reverse = id'.
I second this feeling...
For example, I've never seen (I've not looked hard, though) Quickcheck's testing applied on graphs. Generating "interesting" (whatever that means for your particular problem) graphs doesn't seem to be a trivial test, even if it's a mere data structure... Does anyone know of such examples?
I do some graph-based testing in graphviz [1]. It is non-trivial to generate decent Arbitrary instances due to the recursive definitions :s [1]: http://hackage.haskell.org/package/graphviz -- Ivan Lazar Miljenovic Ivan.Miljenovic@gmail.com IvanMiljenovic.wordpress.com

On Wed, 12 Jan 2011, Serge Le Huitouze wrote:
Also, I wonder how you guys do when you're trying to tests code using a lot of numbers (be them floating point or even integer).
Machine size integers and floating point numbers are indeed nasty. I test a lot in NumericPrelude with QuickCheck, but then I test on Integers and Rationals in order to see whether my algorithms are in principle correct. I can test floating point algorithms only with approximate equality tests and finding general valid tolerances is difficult, as you pointed out.
E.g., integers: A code doing addition and substraction of some sort. A property such as "X = (X add Y) sub Y" is easily falsifiable when the number of bits of your integer is too small for your numbers.
Since fix-width words represent modulo-arithmetic, your law would even hold in case of overflows.

Henning Thielemann
A code doing addition and substraction of some sort. A property such as "X = (X add Y) sub Y" is easily falsifiable when the number of bits of your integer is too small for your numbers.
Since fix-width words represent modulo-arithmetic, your law would even hold in case of overflows.
True in this very example, but it's overly simplistic and I chose it just for the sake of illustration. A property such as "X = (X mul Y) div Y", with Y != 0 (of course ;-), Y prime wrt 2^nbBits (nbBits being the size of your integers), and the intermediate product exceeding 2^nbBits would fail miserably... --Serge

2011/1/12 Serge Le Huitouze
Evan Laforge
wrote: ..... So, in addition to defining the approximation (not always easy as I tried to "demonstrate" above) to be used in comparisons, one probably needs ad'hoc generators whose complexity might very well exceed that of the code one wants to test... So, do you have any "methodology" for such use cases?
For this reason whenever the space of test cases is difficult to cover or when the border cases are not know well due to the complexity of the code, I created the package properties (http://hackage.haskell.org/package/properties) . http://hackage.haskell.org/package/properties The idea is to check the relevant subset of the test space that is precisely the one that the real application generates, so no extra generator coding are needed while at the same time making sure that the property holds for all the real data. It is in essence, a library that permits assertions of properties defined somewhere else . (something that assert does not permit). and a mechanism to create informative messages. I did´nt use it very mucho owever. Theoreticaly I found it a good idea. Alberto
--Serge
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Serge Le Huitouze
So, do you have any "methodology" for such use cases?
QuickCheck has the ==> operator, which lets you add a precondition. So you could limit the testing of your property to values that satisfy the precondition. An alternative is to use newtype with a custom generator to produce only data that makes sense. Of course, ideally you should design your types so that all possible values are meaningful :-) -k -- If I haven't seen further, it is by standing in the footprints of giants
participants (6)
-
Alberto G. Corona
-
Gregory Crosswhite
-
Henning Thielemann
-
Ivan Lazar Miljenovic
-
Ketil Malde
-
Serge Le Huitouze