$30 off During Our Annual Pro Sale. View Details »

Property Based Testing

Property Based Testing

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

Mathias Verraes

April 06, 2016
Tweet

More Decks by Mathias Verraes

Other Decks in Technology

Transcript

  1. Property Based Testing
    @mathiasverraes

    View Slide

  2. View Slide

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

    View Slide

  4. inc x = x + 1

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  8. double x = x * 2

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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]

    View Slide

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

    View Slide

  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.

    View Slide

  15. Split 1
    -- define
    split :: Char -> String -> [String]
    -- so that
    split '@' "[email protected]" == ["foo","example.com"]
    split '/' "/usr/include" == ["", "usr", "include"]
    1 https://www.schoolofhaskell.com/user/pbv/an-introduction-to-quickcheck-testing

    View Slide

  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

    View Slide

  17. 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]

    View Slide

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

    View Slide

  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'

    View Slide

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

    View Slide

  21. Modify the definition of split
    split char [] = []
    ...
    -- becomes
    split char [] = [""]
    ...
    > quickCheck unsplit_inverses_split
    +++ OK, passed 100 tests.

    View Slide

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

    View Slide