Testing
46.1 Hspec
-- test/Spec.hs
import Test.Hspec
import Test.QuickCheck
import Control.Exception (evaluate)
import MyLib (quicksort, safeDiv)
main :: IO ()
main = hspec $ do
describe "quicksort" $ do
it "sorts a simple list" $
quicksort [3, 1, 2] `shouldBe` [1, 2, 3 :: Int]
it "handles the empty list" $
quicksort ([] :: [Int]) `shouldBe` []
it "is idempotent" $ property $
\xs -> quicksort (quicksort xs) == quicksort (xs :: [Int])
it "preserves length" $ property $
\xs -> length (quicksort xs) == length (xs :: [Int])
describe "safeDiv" $ do
it "returns Nothing for division by zero" $
safeDiv 1 0 `shouldBe` Nothing
it "errors on head []" $
evaluate (head ([] :: [Int])) `shouldThrow` anyException46.2 QuickCheck: Property-Based Testing
Shrinking finds minimal failing cases automatically. Test type class laws with property tests (e.g., quickcheck-classes, hedgehog-classes).
Alternatives: Hedgehog (integrated shrinking, better generators), falsify, smallcheck (exhaustive small inputs).
import Test.QuickCheck
import Data.List (sort)
prop_reverseReverse :: [Int] -> Bool
prop_reverseReverse xs = reverse (reverse xs) == xs
prop_sortOrdered :: [Int] -> Bool
prop_sortOrdered xs = ordered (sort xs)
where ordered ys = and (zipWith (<=) ys (drop 1 ys))
prop_insertLength :: Int -> [Int] -> Property
prop_insertLength x xs = classify (null xs) "empty" $ length (x : xs) === length xs + 1
-- Custom generators
data Color = Red | Green | Blue deriving (Show, Eq, Enum, Bounded)
instance Arbitrary Color where
arbitrary = elements [minBound .. maxBound]
newtype Positive' = Positive' Int deriving Show
instance Arbitrary Positive' where
arbitrary = Positive' <$> choose (1, 1000)
shrink (Positive' n) = [Positive' m | m <- shrink n, m > 0]
-- Preconditions
prop_divMod :: Int -> Int -> Property
prop_divMod x y = y /= 0 ==> (x `div` y) * y + (x `mod` y) == x
main :: IO ()
main = do
quickCheck prop_reverseReverse
quickCheck prop_sortOrdered
quickCheck (withMaxSuccess 1000 prop_divMod)
verboseCheck prop_insertLength46.3 Tasty and Other Tools
doctest (run examples in Haddock comments), tasty-golden (golden files), hspec-wai (web apps), HUnit (unit tests), hpc coverage (cabal test --enable-coverage).
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck as QC
main :: IO ()
main = defaultMain $ testGroup "All tests"
[ testCase "addition" $ 2 + 2 @?= (4 :: Int)
, QC.testProperty "reverse involution" (\xs -> reverse (reverse xs) == (xs :: [Int]))
]