liveBook · Manning

propertyTest

As explained in Learn Haskell by Example

propertyTest is a book-defined testing function that evaluates a Boolean predicate against generated random inputs.

propertyTest runs a Boolean predicate against randomly generated values. It takes a predicate, a RandomIO generator, and a test count. Each run generates one test case; a true result continues with one fewer test, while a false result prints the failing value. Reaching zero prints “Tests successful!” section 9.1.6

Listing 9.12 An action for property testing using custom random value generators
propertyTest :: Show a => (a -> Bool) -> RandomIO a -> Int -> IO ()
propertyTest predicate random n
  | n <= 0 = putStrLn "Tests successful!"       #1
  | otherwise = do
    testCase <- runRandomIO random  #2
    if predicate testCase                                     #3
      then propertyTest predicate
 random $ n - 1            #3                                  
      else putStrLn $ "Test failed on: " <> show testCase         #3

The implementation makes the test loop explicit: generate a value, evaluate the property, recurse after success, or report the counterexample after failure. section 9.1.6

Use propertyTest by supplying three things:

  • A property expressed as a function from an input value to Bool. section 9.1.6 section 9.2
  • A generator whose output matches the inputs the property should cover, such as asciiString for human-readable ASCII strings. section 9.1.6 section 9.2
  • A number of random cases to run, such as 100. section 9.2

Checking ROT13 symmetry

ROT13 should return the original string when applied twice. The property \s -> s == rot13 (rot13 s) is tested with asciiString and 100 generated inputs. Because asciiString includes human-readable ASCII characters rather than only letters, the check also probes whether the symmetry remains robust across those characters. section 9.2

A property test can protect a specification during refactoring: if a change breaks the stated property, a generated failing input can expose it. Properties may also test helper functions, such as whether alphabetRot always returns a character contained in its supplied alphabet. Naming property-test functions with a prop prefix can distinguish them from example-based unit tests. section 9.2 section 9.2

Listing 9.13 A property test for the symmetry of the ROT13 function
propRot13Symmetrical :: IO ()
propRot13Symmetrical =
  propertyTest
    (\s -> s == rot13 (rot13 s))  #1
    asciiString  #2
    100  #3

This complete property test combines a predicate, the asciiString generator, and 100 runs in one IO action. section 9.2

How Learn Haskell by Example covers it

How deep it goes
Explains the recursive test action and generator selection, then applies it to ROT13 and alphabet rotation properties.
Examples
Uses ROT13 applied twice and alphabetRot returning a character from its alphabet.
What it assumes
Uses Boolean predicates, IO actions, functions, and custom RandomIO generators without stating further prerequisites.
What you can do after
Apply it to express and run randomized specification checks with custom generators.
How it connects
Connects RandomIO, asciiString, suchThat, ROT13, alphabetRot, and example-based unit tests.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Haskell by Example. The section links point to the parts of the book it draws on.