{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} -- | This module defines the 'MockableBase' and 'Mockable' classes that are -- needed to use an MTL-style type class with 'Test.HMock.MockT.MockT'. You -- will typically derive 'MockableBase' with Template Haskell, since it's mostly -- boilerplate. The 'Mockable' class adds a customizable setup method which you -- can define yourself to add the right defaults for methods in the mocked -- class. module Test.HMock.Mockable ( Mockable (..), MockableBase (..), MatchResult (..), ) where import Control.Monad.Trans (MonadIO) import Data.Kind (Constraint, Type) import Data.Typeable (Typeable) import GHC.TypeLits (Symbol) import {-# SOURCE #-} Test.HMock.Internal.State (MockSetup) -- | The result of matching a @'Matcher' a@ with an @'Action' b@. Because the -- types should already guarantee that the methods match, all that's left is to -- match arguments. data MatchResult where -- | No match. The arg is explanations of mismatch. NoMatch :: [(Int, String)] -> MatchResult -- | Match. Stores a witness to the equality of return types. Match :: MatchResult -- | A base class for 'Monad' subclasses whose methods can be mocked. You -- usually want to generate this instance using 'Test.HMock.TH.makeMockable', -- 'Test.HMock.TH.makeMockable', or 'Test.HMock.TH.makeMockableWithOptions', -- since it's just boilerplate. class (Typeable cls) => MockableBase (cls :: (Type -> Type) -> Constraint) where -- | An action that is performed. This data type will have one constructor -- for each method. data Action cls :: Symbol -> (Type -> Type) -> Type -> Type -- | A specification for matching actions. The actual arguments should be -- replaced with predicates. data Matcher cls :: Symbol -> (Type -> Type) -> Type -> Type -- | Gets a text description of an 'Action', for use in error messages. showAction :: Action cls name m a -> String -- | Gets a text description of a 'Matcher', for use in error messages. showMatcher :: Maybe (Action cls name m a) -> Matcher cls name m b -> String -- | Attempts to match an 'Action' with a 'Matcher'. matchAction :: Matcher cls name m a -> Action cls name m a -> MatchResult -- | A class for 'Monad' subclasses whose methods can be mocked. This class -- augments 'MockableBase' with a setup method that is run before HMock touches -- the 'Monad' subclass for the first time. The default implementation does -- nothing, but you can derive your own instances that add setup behavior. class MockableBase cls => Mockable (cls :: (Type -> Type) -> Constraint) where -- | An action to run and set up defaults for this class. The action will be -- run before HMock touches the class, either to add expectations or to -- delegate a method. -- -- By default, unexpected actions throw errors, and actions with no explicit -- default always return the default value of their return type, or -- 'undefined' if there is none. You can change this on a per-class or -- per-test basis. -- -- * To change defaults on a per-class basis, you should use -- 'Test.HMock.MockT.allowUnexpected' and/or 'Test.HMock.MockT.byDefault' -- to perform the setup you need here. -- * To change defaults on a per-test basis, you should use -- 'Test.HMock.MockT.allowUnexpected' and/or 'Test.HMock.MockT.byDefault' -- directly from the test. setupMockable :: (MonadIO m, Typeable m) => proxy cls -> MockSetup m () setupMockable _ = return ()