Using FitSpec to guide property creation ---------------------------------------- Suppose we want to write test properties for the function `sort`, but we do not know where to start. We can use FitSpec to guide property creation. We first import what is needed: import Test.FitSpec import Data.List (sort) Then we need a property list function: given a sorting implementation, return the properties applied to *that* implementation. Since we don't have any properties, we will start by returning and empty list: properties :: (Show a, Ord a, Listable a) => ([a] -> [a]) -> [Properties] properties sort' = [] Then, we need a main function, that calls the FitSpec's `report` function, which will report the results of mutation testing. It needs a function to be mutated and the property list. main = report (sort::[Int]->[Int]) properties Optionally, for a nicer output, you might want to use the reportWith function, which allows specifying function and argument names (among other options): main = reportWith args { callNames = ["sort xs"] } (sort::[Int]->[Int]) properties By having the three sections above in a file called sorting.hs, we then compile and run: $ ghc -ipath/to/leancheck:path/to/fitspec sorting.hs [9 of 9] Compiling Main ( sorting.hs, sorting.o ) Linking sorting ... $ ./sorting Results based on at most 4000 test cases for each of 2000 mutant variations. Property #Survivors Smallest or simplest sets (%Killed) surviving mutant [] 2000 (0%) \xs -> case xs of [] -> [0] _ -> sort xs The output is self-explanatory. Obviously, our empty property set `[]` did not kill any mutant (`0%`). In other words, all of the `2000` mutants survived. (The actual number of mutants tested will vary depending on your machine, it will probably be higher than 2000 *in this case*, by default FitSpec runs for at least 5 seconds.) The surviving mutant shown on the third column is clearly not a valid implementation of sort. For the empty list, it returns `[0]`. We should improve our property set by killing that mutant. Lets start very simple by adding a property stating that sorting an empty list must yield an empty list: properties sort' = [ property $ sort' [] == [] ] Above, we need to apply the function `property` to each property in the list. Now: $ ./sorting Results based on at most 4000 test cases for each of 2000 mutant variations. Property #Survivors Smallest or simplest sets (%Killed) surviving mutant [1] 984 (49%) \xs -> case xs of [0] -> [] _ -> sort xs [] 2000 (0%) \xs -> case xs of [] -> [0] _ -> sort xs The last row of results is the same as before (all mutants still obviously survive the empty property set). The *first row* show that there are `984` *surviving mutants* (`49%`) for the first property `[1]`: the smallest one is shown on the third column. It sorts `[0]` to `[]`, which is not valid. Lets still be very simple -- sorting a list with one value must yield a list with the same value: properties sort' = [ property $ sort' [] == [] , property $ \x -> sort' [x] == [x] ] Note that, our new property (2) has a free variable. Now: $ ./sorting Results based on at most 1000 test cases for each of 500 mutant variations. Property #Survivors Smallest or simplest sets (%Killed) surviving mutant [1,2] 134 (73%) \xs -> case xs of [0,0] -> [] _ -> sort xs ... Only 27% of mutants to go, perhaps a property stating that the length of the sorted list should not change? properties sort' = [ property $ sort' [] == [] , property $ \x -> sort' [x] == [x] , property $ \xs -> length (sort' xs) == length xs ] Now: $ ./sorting Results based on at most 1000 test cases for each of 500 mutant variations. Property #Survivors Smallest or simplest sets (%Killed) surviving mutant [2,3] 12 (97%) \xs -> case xs of [0,0] -> [0,1] _ -> sort xs ... Conjectures based on at most 1000 test cases for each of 500 mutant variations: [3] ==> [1] 95% killed (likely) The first row show that the current candidate minimal-complete propety-set kills all but `4` mutants and is composed only by properties 2 and 3 (`[2,3]`). When possible, FitSpec also reports *conjectures* based on test results. In this case, that property `sort [] == []` (1) follows from the length property (3). Since that is *clearly* true, we can safely remove that property. properties sort' = [ property $ \x -> sort' [x] == [x] , property $ \xs -> length (sort' xs) == length xs , property $ \x xs -> elem x (sort' xs) == elem x xs ] Now: $ ./sorting Property #Survivors Smallest or simplest sets (%Killed) surviving mutant [2,3] 2 (99%) \xs -> case xs of [0,1] -> [1,0] _ -> sort xs ... Conjectures based on at most 1000 test cases for each of 500 mutant variations: [2,3] ==> [1] 99% killed (possible+) We could go on, but *at this point, you probably got how it works*. As an exercise you can try to improve our property-set over `sort` by killing the above mutant by adding a new property. Later, you can try to improve the results by increasing the time limit (`minimumTime = 10` on args).