testingquickcheckproperty-based-testing

How to come up with properties in propety based testing


This question applies to any particular programming language.

Let us imagine I have a function that converts a data structure to a string of hex.

func toHex(input: data) => string

Let's say I now want to use property based testing to test this. How do I go about coming up with properties upon which to base the test on?

All the examples I am seeing around property based testing assumes very simple mathematical relationship. Things like testing reversing a list or if a string is a sub string of another.

But in the case of toHex function above, I cannot think of any workable way to come up with a property since it look like the only relationship is that some data gets converted to hex string. Well apart from implementing the conversion functionality of finding another library that does something similar and use that for the assertion in the test. But doing this will defeat using property based testing.

Any ideas or suggestions on how to approach property based testing and how to come up with properties to use in the test in the case of such a toHex function?


Solution

  • Believe it or not, this is actually a fairly common case for which property-based testing is useful - with one addition:

    A function to turn a data structure into a string (or JSON, or XML, or something similar) is rarely useful on its own. Often, you'll need its counterpart: a deserializer or parser.

    When you have such a pair of functions, you can write a kind property that Scott Wlaschin calls There and back again: You generate test data as input for the serializer, then call the serializer (toHex), and then again call the parser/deserializer (fromHex?) with the serialized data. The property is, then, that the output from fromHex should be equal to the input to toHex.

    It's my preferred testing strategy when writing serializer/parser pairs. You can see a minimal example in my article Types + Properties = Software: other properties.

    Sometimes, depending on the problem being solved, the There and back again property can serve as a partial description of the serializer, when combined with other properties. Here's a Haskell QuickCheck example of a partial description of the FizzBuzz kata:

    testProperty "Numbers round-trip" $ \ (i :: Int) ->
      let range = [i..i+2]
     
          actual = fizzBuzz <$> range
     
          numbers = catMaybes $ readMaybe <$> actual
      in counterexample
          (show range ++ "->" ++ show actual) $
          all (`elem` range) numbers
    

    The example is taken from my article Property-based testing is not the same as partition testing.

    FizzBuzz is a problem similar to toHex. The input is a 'data structure' ((Integral a, Show a)) and the output is a String (although not hex-encoded).

    Parsers are usually partial functions that return Maybe or Either values, so 'round-trip' assertions will often need to verify something like Just input === output.