hspec-quickcheck-classes-0.0.0.0: components/lib/Test/Hspec/QuickCheck/Classes.hs
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Test.Hspec.QuickCheck.Classes
( testLaws
) where
import Data.Kind
( Type
)
import Data.Proxy
( Proxy (Proxy)
)
import Data.Typeable
( Typeable
, typeRep
)
import GHC.Stack
( HasCallStack
)
import Test.Hspec
( Spec
)
import Test.QuickCheck
( Property
)
import Test.QuickCheck.Classes
( Laws (lawsProperties, lawsTypeclass)
)
import qualified Test.Hspec as Hspec
_importsRequiredForHaddock :: ()
_importsRequiredForHaddock = undefined
where
_type :: Type
_type = undefined
-- | Tests that a type satisfies the laws of one or more typeclasses.
--
-- For example, to test that 'Bool' satisfies the laws of 'Eq', 'Ord', and
-- 'Show':
--
-- @
-- 'testLaws' \@'Bool'
-- [ 'Test.QuickCheck.Classes.eqLaws'
-- , 'Test.QuickCheck.Classes.ordLaws'
-- , 'Test.QuickCheck.Classes.showLaws'
-- ]
-- @
--
-- === Kind polymorphism
--
-- The 'testLaws' function can also test instances of type classes whose type
-- parameters are not of kind 'Type'.
--
-- For example, with 'Maybe', which has kind @'Type' -> 'Type'@:
--
-- @
-- 'testLaws' \@'Maybe'
-- [ 'Test.QuickCheck.Classes.applicativeLaws'
-- , 'Test.QuickCheck.Classes.functorLaws'
-- , 'Test.QuickCheck.Classes.monadLaws'
-- , 'Test.QuickCheck.Classes.foldableLaws'
-- , 'Test.QuickCheck.Classes.traversableLaws'
-- ]
-- @
--
-- And with 'Either', which has kind @'Type' -> 'Type' -> 'Type'@:
--
-- @
-- 'testLaws' \@'Either'
-- [ 'Test.QuickCheck.Classes.bifoldableLaws'
-- , 'Test.QuickCheck.Classes.bifunctorLaws'
-- , 'Test.QuickCheck.Classes.bitraversableLaws'
-- ]
-- @
testLaws
:: forall a
. Typeable a
=> HasCallStack
=> [Proxy a -> Laws]
-> Spec
testLaws = Hspec.describe specDescription . mapM_ testLawsFor
where
specDescription :: String
specDescription = unwords ["Testing laws for", typeName]
where
typeName :: String
typeName = show $ typeRep $ Proxy @a
testLawsFor :: (Proxy a -> Laws) -> Spec
testLawsFor toLaws =
Hspec.describe typeclassName $
mapM_ testNamedProperty namedProperties
where
laws :: Laws
laws = toLaws Proxy
namedProperties :: [(String, Property)]
namedProperties = lawsProperties laws
testNamedProperty :: (String, Property) -> Spec
testNamedProperty (name, property) = Hspec.it name property
typeclassName :: String
typeclassName = lawsTypeclass laws