Test.QuickCheck.Monadic

QuickCheck-2.8.2: Automatic testing of Haskell programs

Safe Haskell | Safe
Language | Haskell98

Test.QuickCheck.Monadic

Contents

- Property monad
- Monadic specification combinators
- Run functions

Description

Allows testing of monadic values. Will generally follow this form:

    prop_monadic a b = monadicIO $ do
      a' <- run (f a)
      b' <- run (f b)
      -- ...
      assert someBoolean

Example using the `FACTOR(1)` command-line utility:

    import System.Process
    import Test.QuickCheck
    import Test.QuickCheck.Monadic

    -- $ factor 16
    -- 16: 2 2 2 2
    factor :: Integer -> IO [Integer]
    factor n = parse `fmap` readProcess "factor" [show n] "" where

      parse :: String -> [Integer]
      parse = map read . tail . words

    prop_factor :: Positive Integer -> Property
    prop_factor (Positive n) = monadicIO $ do
      factors <- run (factor n)

      assert (product factors == n)

    >>> quickCheck prop_factor
    +++ OK, passed 100 tests.

See the paper "Testing Monadic Code with QuickCheck".

Synopsis

- newtype PropertyM m a = MkPropertyM {
  - unPropertyM :: (a -> Gen (m Property)) -> Gen (m Property)
  }
- run :: Monad m => m a -> PropertyM m a
- assert :: Monad m => Bool -> PropertyM m ()
- pre :: Monad m => Bool -> PropertyM m ()
- wp :: Monad m => m a -> (a -> PropertyM m b) -> PropertyM m b
- pick :: (Monad m, Show a) => Gen a -> PropertyM m a
- forAllM :: (Monad m, Show a) => Gen a -> (a -> PropertyM m b) -> PropertyM m b
- monitor :: Monad m => (Property -> Property) -> PropertyM m ()
- stop :: (Testable prop, Monad m) => prop -> PropertyM m a
- monadic :: Monad m => (m Property -> Property) -> PropertyM m a -> Property
- monadic' :: Monad m => PropertyM m a -> Gen (m Property)
- monadicIO :: PropertyM IO a -> Property
- monadicST :: (forall s. PropertyM (ST s) a) -> Property
- runSTGen :: (forall s. Gen (ST s a)) -> Gen a

# Property monad

newtype PropertyM m a

The property monad is really a monad transformer that can contain monadic computations in the monad `m` it is parameterized by:

- `m` - the `m`-computations that may be performed within `PropertyM`

Elements of `PropertyM m a` may mix property operations and `m`-computations.

Constructors

MkPropertyM |
Fields unPropertyM :: (a -> Gen (m Property)) -> Gen (m Property)

Instances

MonadTrans PropertyM |
Monad m => Monad (PropertyM m) |
Functor (PropertyM m) |
Monad m => Applicative (PropertyM m) |
MonadIO m => MonadIO (PropertyM m) |

# Monadic specification combinators

run :: Monad m => m a -> PropertyM m a

The lifting operation of the property monad. Allows embedding monadic/`IO`-actions in properties:

    log :: Int -> IO ()

    prop_foo n = monadicIO $ do
      run (log n)
      -- ...

assert :: Monad m => Bool -> PropertyM m ()

Allows embedding non-monadic properties into monadic ones.

pre :: Monad m => Bool -> PropertyM m ()

Tests preconditions. Unlike `assert` this does not cause the property to fail, rather it discards them just like using the implication combinator `==>`.

This allows representing the Hoare triple

    {p} x ← e{q}

as

    pre p
    x <- run e
    assert q

wp :: Monad m => m a -> (a -> PropertyM m b) -> PropertyM m b

The weakest precondition

    wp(x ← e, p)

can be expressed as in code as `wp e (\x -> p)`.

pick :: (Monad m, Show a) => Gen a -> PropertyM m a

Quantification in a monadic property, fits better with do-notation than `forAllM`.

forAllM :: (Monad m, Show a) => Gen a -> (a -> PropertyM m b) -> PropertyM m b

An alternative to quantification a monadic properties to `pick`, with a notation similar to `forAll`.

monitor :: Monad m => (Property -> Property) -> PropertyM m ()

Allows making observations about the test data:

    monitor (collect e)

collects the distribution of value of `e`.

    monitor (counterexample "Failure!")

Adds `"Failure!"` to the counterexamples.

stop :: (Testable prop, Monad m) => prop -> PropertyM m a

# Run functions

monadic :: Monad m => (m Property -> Property) -> PropertyM m a -> Property

monadic' :: Monad m => PropertyM m a -> Gen (m Property)

monadicIO :: PropertyM IO a -> Property

Runs the property monad for `IO`-computations.

    prop_cat msg = monadicIO $ do
      (exitCode, stdout, _) <- run (readProcessWithExitCode "cat" [] msg)

      pre (ExitSuccess == exitCode)

      assert (stdout == msg)

    >>> quickCheck prop_cat
    +++ OK, passed 100 tests.

monadicST :: (forall s. PropertyM (ST s) a) -> Property

Runs the property monad for `ST`-computations.

    -- Your mutable sorting algorithm here
    sortST :: Ord a => [a] -> ST s (MVector s a)
    sortST = thaw . fromList . sort

    prop_sortST xs = monadicST $ do
      sorted  <- run (freeze =<< sortST xs)
      assert (toList sorted == sort xs)

    >>> quickCheck prop_sortST
    +++ OK, passed 100 tests.

runSTGen :: (forall s. Gen (ST s a)) -> Gen a