Mathias Verraes
April 06, 2016
2.4k

Property Based Testing

5min lightning talk for the SoCraTes Belgium meetup and CukeUp London.

April 06, 2016

Transcript

1. Property Based Testing
@mathiasverraes

2. function inc(x) { return x + 1; }

3. inc x = x + 1

4. -- Tests
double 1 `should_be` 2
double 2 `should_be` 4
-- Implementation
double x
| x == 1 = 2
| x == 2 = 4

5. A property of double
double_is_always_even :: Int -> Bool
double_is_always_even x = even (double x)

6. > quickCheck double_is_always_even
Failed: 0
(after 1 test)
Exception:
Non-exhaustive patterns in function double

7. double x = x * 2

8. quickCheck double_is_always_even
Passed: 0
Passed: 1
Passed: -3
Passed: -1
(...)
Passed: -58
Passed: 89
+++ OK, passed 100 tests.

9. More properties of double
double_compare_to_input x
| x > 0 = double x > x
| x < 0 = double x < x
| x == 0 = True
double_minus_input x = double x - x == x

10. Distributivity Law
reverse_is_distributive xs ys =
reverse (xs++ys) == reverse xs ++ reverse ys

11. > quickCheck reverse_is_distributive
Passed: [] []
Passed: [] [1]
Passed: [1] []
Failed: [3,-3] [0,2,0]
Passed: [] [0,2,0]
Failed: [3] [0,2,0]
Failed: [-3] [0,2,0]
Failed: [0] [0,2,0]
Failed: [0] [2,0]
(...)
Falsifiable (after 4 tests and 6 shrinks): [0] [1]

12. Oops...
reverse_is_distributive xs ys =
reverse (xs++ys) == reverse ys ++ reverse xs

13. Passed: [] []
Passed: [1,-2] [0]
Passed: [-2] []
(...)
Passed:
[-34,44,-58,-41,-17,-53,-14,27,54,46,-10,-46,-20,46]
[-9,-32,-47,50,43,-47,-43,-61,37,4,-59,48,34]
+++ OK, passed 100 tests.

14. Split 1
-- define
split :: Char -> String -> [String]
-- so that
split '@' "[email protected]" == ["foo","example.com"]
split '/' "/usr/include" == ["", "usr", "include"]

15. -- splitting an empty list results in an empty list
split char [] = []
split char str
| null after = before : []
| otherwise = before : split char (tail after)
where
before = takeWhile (/=char) str
after = dropWhile (/=char) str

16. Property:
Splitting and unsplitting
-- test
unsplit '@' ["foo","example.com"] == "[email protected]"
unsplit '/' ["", "usr", "include"] == "/usr/include"
--implementation
unsplit :: Char -> [String] -> String
unsplit char = concat . intersperse [char]

17. -- property
unsplit_inverses_split str =
forAll (elements str)
(\char -> unsplit char (split char str) == str)

18. > quickCheck unsplit_inverses_split
Passed: ""
Passed: "\252\210" '\252'
Passed: "\163^\EOT" '\163'
Failed: "y" 'y'
Failed: "a" 'a'
Falsifiable (after 6 tests and 1 shrink): "a" 'a'

19. split 'a' "a" == [""]
unsplit 'a' [""] == ""
-- should be:
split 'a' "a" == ["", ""]
unsplit 'a' ["", ""] == "a"

20. Modify the deﬁnition of split
split char [] = []
...
-- becomes
split char [] = [""]
...
> quickCheck unsplit_inverses_split
+++ OK, passed 100 tests.

21. Property Based Testing
→ test lots of random cases cheaply
→ encourage thinking about general properties
Thanks :-)
@mathiasverraes