Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Property Based Testing

Property Based Testing

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

330627d5f564b710721236077903ed60?s=128

Mathias Verraes

April 06, 2016
Tweet

Transcript

  1. Property Based Testing @mathiasverraes

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

  4. inc x = x + 1

  5. -- Tests double 1 `should_be` 2 double 2 `should_be` 4

    -- Implementation double x | x == 1 = 2 | x == 2 = 4
  6. A property of double double_is_always_even :: Int -> Bool double_is_always_even

    x = even (double x)
  7. > quickCheck double_is_always_even Failed: 0 (after 1 test) Exception: Non-exhaustive

    patterns in function double
  8. double x = x * 2

  9. quickCheck double_is_always_even Passed: 0 Passed: 1 Passed: -3 Passed: -1

    (...) Passed: -58 Passed: 89 +++ OK, passed 100 tests.
  10. 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
  11. Distributivity Law reverse_is_distributive xs ys = reverse (xs++ys) == reverse

    xs ++ reverse ys
  12. > 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]
  13. Oops... reverse_is_distributive xs ys = reverse (xs++ys) == reverse ys

    ++ reverse xs
  14. 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.
  15. Split 1 -- define split :: Char -> String ->

    [String] -- so that split '@' "foo@example.com" == ["foo","example.com"] split '/' "/usr/include" == ["", "usr", "include"] 1 https://www.schoolofhaskell.com/user/pbv/an-introduction-to-quickcheck-testing
  16. -- 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
  17. Property: Splitting and unsplitting -- test unsplit '@' ["foo","example.com"] ==

    "foo@example.com" unsplit '/' ["", "usr", "include"] == "/usr/include" --implementation unsplit :: Char -> [String] -> String unsplit char = concat . intersperse [char]
  18. -- property unsplit_inverses_split str = forAll (elements str) (\char ->

    unsplit char (split char str) == str)
  19. > quickCheck unsplit_inverses_split Passed: "" Passed: "\252\210" '\252' Passed: "\163^\EOT"

    '\163' Passed: "v\RSs" 'v' Failed: "y" 'y' Failed: "a" 'a' Falsifiable (after 6 tests and 1 shrink): "a" 'a'
  20. split 'a' "a" == [""] unsplit 'a' [""] == ""

    -- should be: split 'a' "a" == ["", ""] unsplit 'a' ["", ""] == "a"
  21. Modify the definition of split split char [] = []

    ... -- becomes split char [] = [""] ... > quickCheck unsplit_inverses_split +++ OK, passed 100 tests.
  22. Property Based Testing → test lots of random cases cheaply

    → encourage thinking about general properties Thanks :-) @mathiasverraes