9

I am using Haskell and QuickCheck to write a test for the following function:

{-| Given a list of points and a direction, find the point furthest
along in that direction. -}

fn :: (Eq a, Ord a, DotProd a) => [a] -> a -> a
fn pnts dir = pnts !! index
    where index = fromJust $ elemIndex (maximum dotproducts) dotproducts
          dotproducts = map (dot dir) pnts

I believe this implementation to be correct, since it's not too complex of a function. But, I want to use QuickCheck to test it for some edge cases.

However, I run into the problem that, when I define my QuickCheck tests, they are identical to the function I am testing.

How do I write a test in QuickCheck that tests the purpose of a function without repeating its implementation?

sdasdadas
  • 23,917
  • 20
  • 63
  • 148
  • If someone feels the question is too broad, please consider this a question specifically aimed at the function `fn`. – sdasdadas Feb 16 '14 at 03:03
  • 1
    It would help if you shared a bit more information about your problem, such as the specific inadequate test, what you expect from it, and the source of the DotProd typeclass. – Stephen Diehl Feb 16 '14 at 05:17
  • Can you write an `Arbitrary` for a point? A direction? If so, what property of the randomly generated points is unsatisfactory? – user2407038 Feb 16 '14 at 05:21
  • Please add more information so that your code is [self-contained](http://sscce.org/). In particular, the definition of `DotProd` and some its instances. It'd also help if you added the QuickCheck tests you tried. – Petr Feb 16 '14 at 07:09
  • A coding tip: have a look at the `maximumBy` function in `Data.List`. Using `fromJust` is unsafe since it can throw an exception. – ErikR Feb 16 '14 at 09:26

1 Answers1

13

How do I write a test in QuickCheck that tests the purpose of a function without repeating its implementation?

First, note that sometimes, a quickcheck property that states that a function behaves according to its current implementation is not completely worthless. You can use it for regression tests if you ever change the implementation. For example, if you optimize the definition of your fn to use clever data structures, the Quickcheck property based on the old, more straight-forward implementation might prove helpful.

Second, you often want your Quickcheck properties to check high-level and declarative properties of your functions, while the implementation is usually lower-level and more directly executable. In this case, you could specify such properties as:

  • forall lists of points ps and directions d, the point fn ps d is in the list ps.

  • forall lists of points ps, directions d, and forall points p in ps, the point p is not further along in the direction d than the point fn ps d.

  • forall points p and forall directions d, fn [p, origin] d is p.

I'm not totally sure about the underlying geometry, so my examples might be stupid. But I hope the examples convey the general idea: The quickcheck properties could check for properties of the specification "being further along in a direction" without mentioning the particular algorithm.

Toxaris
  • 7,156
  • 1
  • 21
  • 37