Test.QuickCheck.Monadic

QuickCheck-2.14.2: Automatic testing of Haskell programs

Safe Haskell | Safe
Language | Haskell2010

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 :: (Testable a, Monad m) => (m Property -> Property) -> PropertyM m a -> Property
- monadic' :: (Testable a, Monad m) => PropertyM m a -> Gen (m Property)
- monadicIO :: Testable a => PropertyM IO a -> Property
- monadicST :: Testable a => (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

Instances details

MonadTrans PropertyM # |
Instance details Defined in Test.QuickCheck.Monadic Methods lift :: Monad m => m a -> PropertyM m a #
Monad m => MonadFail (PropertyM m) # |
Instance details Defined in Test.QuickCheck.Monadic Methods fail :: String -> PropertyM m a #
MonadIO m => MonadIO (PropertyM m) # |
Instance details Defined in Test.QuickCheck.Monadic Methods liftIO :: IO a -> PropertyM m a #
Applicative (PropertyM m) # |
Instance details Defined in Test.QuickCheck.Monadic Methods pure :: a -> PropertyM m a # (<*>) :: PropertyM m (a -> b) -> PropertyM m a -> PropertyM m b # liftA2 :: (a -> b -> c) -> PropertyM m a -> PropertyM m b -> PropertyM m c # (*>) :: PropertyM m a -> PropertyM m b -> PropertyM m b # (<*) :: PropertyM m a -> PropertyM m b -> PropertyM m a #
Functor (PropertyM m) # |
Instance details Defined in Test.QuickCheck.Monadic Methods fmap :: (a -> b) -> PropertyM m a -> PropertyM m b # (<$) :: a -> PropertyM m b -> PropertyM m a #
Monad m => Monad (PropertyM m) # |
Instance details Defined in Test.QuickCheck.Monadic Methods (>>=) :: PropertyM m a -> (a -> PropertyM m b) -> PropertyM m b # (>>) :: PropertyM m a -> PropertyM m b -> PropertyM m b # return :: a -> PropertyM m a #

# 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`. Note: values generated by `pick` do not shrink.

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

Quantification in monadic properties to `pick`, with a notation similar to `forAll`. Note: values generated by `forAllM` do not shrink.

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 :: (Testable a, Monad m) => (m Property -> Property) -> PropertyM m a -> Property #

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

monadicIO :: Testable a => 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 :: Testable a => (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 #