method-0.4.0.0: rebindable methods for improving testability
LicenseBSD-3
Maintainerautotaker@gmail.com
Stabilityexperimental
Safe HaskellNone
LanguageHaskell2010

Test.Method

Description

 
Synopsis

Documentation

This module provides DSLs for mocking methods and for validating method calls

Mocking monomorphic methods

Usage

fizzbuzz :: Int -> IO String
fizzbuzz = mockup $ do
  when (args (\x -> mod x 15 == 0)) `thenReturn` "fizzbuzz"
  when (args (\x -> mod x 3 == 0)) `thenReturn` "fizz"
  when (args (\x -> mod x 5 == 0)) `thenReturn` "buzz"
  when (args (>=0)) `thenMethod` (\x -> pure $ show x)
  throwNoStub $ when anything
>>> fizzbuzz 0
"fizzbuzz"
>>> fizzbuzz 1
"1"
>>> fizzbuzz 3
"fizz"
>>> fizzbuzz 5
"buzz"
>>> fizzbuzz (-1)
*** Exception: no stub found for argument: -1
CallStack (from HasCallStack):
 error, called at src/Test/Method/Mock.hs:98:9 in method-0.2.0.0-inplace:Test.Method.Mock"

References

mockup :: Method method => Mock method -> method Source #

generate a method from Mock DSL. Mock DSL consists of rules. On a call of generated method, the first rule matched the arguments is applied.

thenReturn :: (Behave x, Method (MethodOf x)) => Condition x -> Ret (MethodOf x) -> x Source #

Specify behavior that return a constant value for a call

thenAction :: (Behave x, Method (MethodOf x)) => Condition x -> Base (MethodOf x) (Ret (MethodOf x)) -> x Source #

Specify behavior that executes an action for a call

thenMethod :: Behave x => Condition x -> MethodOf x -> x Source #

Specify behavior from a pair of a condition and a method.

throwNoStubWithShow :: Method method => (Args method -> String) -> (Args method -> Bool) -> Mock method Source #

throwNoStubWithShow fshow matcher means the method raises runtime exception if the arguments matches matcher. The argument tuple is converted to String by using fshow function.

throwNoStub :: (Method method, Show (AsTuple (Args method)), TupleLike (Args method)) => Matcher (Args method) -> Mock method Source #

throwNoStub matcher means the method raises a runtime exception if the arguments matches matcher. The argument tuple is converted to String by using show function.

Mocking polymorphic methods

Usage

Often you want to mock polymorphic functions. For example, assume that we are testing the following method.

type QueryFunc = forall q r. (ToRow q, FromRow r) => Query -> q -> IO [r]
service :: QueryFunc -> Day -> IO [Event]
service query today = do
  events <- query "SELECT * FROM event WHERE date = ?" (Only today)
  pure events

Because QueryFunc is a polymorphic function, it is impossible to mock directly with mockup.

In order to mock QueryFunc, first add Typeable (and Show) constraint(s) for each type variables.

type QueryFunc = forall q r. (ToRow q, Typeable q, Show q, FromRow r, Typeable r, Show r) => Query -> q -> IO [r]

Next, we mock dynamic version of QueryFunc, where each type variable is replaced with DynamicShow (or Dynamic). Finally, we obtain polymorphic method by casting the dynamic version with castMethod.

queryDyn :: Query -> DynamicShow -> IO [DynamicShow]
queryDyn = mockup $ ...
queryMock :: QueryFunc
queryMock = castMethod queryDyn

Now you can write test for service as follows.

spec :: Spec
spec = do
  describe "service" $ do
    it "return events whose dates are equal to today" $ do
      let today = fromGregorian 2020 2 20
          sql = "SELECT * FROM event WHERE date = ?"
          events = [Event 0, Event 1]
          queryDyn :: Query -> DynamicShow -> IO [DynamicShow]
          queryDyn = mockup $
            when (args ((==sql), dynArg (==today))) `thenReturn` toDyn events
      service (castMethod queryDyn) today `shouldReturn` events

data DynamicShow Source #

Dynamic value whose content is showable. Using this type instead of Dynamic is recommended because it gives better error messages.

Instances

Instances details
Show DynamicShow Source # 
Instance details

Defined in Test.Method.Dynamic

DynamicLike DynamicShow Source # 
Instance details

Defined in Test.Method.Dynamic

(Typeable a, Show a) => ToDyn DynamicShow a Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: a -> DynamicShow Source #

(Typeable a, Show a) => FromDyn DynamicShow a Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: DynamicShow -> a Source #

data Dynamic #

A value of type Dynamic is an object encapsulated together with its type.

A Dynamic may only represent a monomorphic value; an attempt to create a value of type Dynamic from a polymorphically-typed expression will result in an ambiguity error (see toDyn).

Showing a value of type Dynamic returns a pretty-printed representation of the object's type; useful for debugging.

Instances

Instances details
Show Dynamic

Since: base-2.1

Instance details

Defined in Data.Dynamic

Exception Dynamic

Since: base-4.0.0.0

Instance details

Defined in Data.Dynamic

DynamicLike Dynamic Source # 
Instance details

Defined in Test.Method.Dynamic

Typeable a => ToDyn Dynamic a Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: a -> Dynamic Source #

Typeable a => FromDyn Dynamic a Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: Dynamic -> a Source #

castMethod :: (ToDyn (Args method) (Args method'), FromDyn (Ret method) (Ret method'), Method method, Method method', Base method ~ Base method') => method -> method' Source #

convert a dynamically-typed method to a polymorphic method.

  fDyn :: String -> DynamicShow -> Dynamic -> IO [DynamicShow]
  fDyn = ...
  fPoly :: (Typeable a, Show a, Typeable b, Typeable c, Show c) => String -> a -> b -> IO [c]
  fPoly = castMethod fDyn
  

dynArg :: (Typeable a, DynamicLike b) => Matcher a -> Matcher b Source #

Convert given matcher to dynamic matcher. The dynamic matcher matches a dynamic value only if the value has the type of given matcher.

class FromDyn a b where Source #

FromDyn a b provides a function to convert type a to type b, where b is a type whose dynamic type occurences are replaced by concrete types.

For example: FromDyn (Int, Dynamic, Maybe Dynamic) (Int, Bool, Maybe String)

Minimal complete definition

Nothing

Methods

fromDyn :: a -> b Source #

convert dynamic value to specified type. It thows runtime exception if the dynamic value does not have specified type.

default fromDyn :: (Generic a, Generic b, FromDyn' (Rep a) (Rep b)) => a -> b Source #

Instances

Instances details
FromDyn a a Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: a -> a Source #

Typeable a => FromDyn Dynamic a Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: Dynamic -> a Source #

(Typeable a, Show a) => FromDyn DynamicShow a Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: DynamicShow -> a Source #

FromDyn a b => FromDyn [a] [b] Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: [a] -> [b] Source #

FromDyn a b => FromDyn (Maybe a) (Maybe b) Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: Maybe a -> Maybe b Source #

(ToDyn a a', FromDyn b b') => FromDyn (a -> b) (a' -> b') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: (a -> b) -> a' -> b' Source #

(FromDyn a a', FromDyn b b') => FromDyn (Either a b) (Either a' b') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: Either a b -> Either a' b' Source #

(FromDyn a a', FromDyn b b') => FromDyn (a, b) (a', b') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: (a, b) -> (a', b') Source #

(FromDyn a b, FromDyn c d) => FromDyn (a :* c) (b :* d) Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: (a :* c) -> b :* d Source #

(FromDyn a a', FromDyn b b', FromDyn c c') => FromDyn (a, b, c) (a', b', c') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: (a, b, c) -> (a', b', c') Source #

(FromDyn a a', FromDyn b b', FromDyn c c', FromDyn d d') => FromDyn (a, b, c, d) (a', b', c', d') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: (a, b, c, d) -> (a', b', c', d') Source #

(FromDyn a a', FromDyn b b', FromDyn c c', FromDyn d d', FromDyn e e') => FromDyn (a, b, c, d, e) (a', b', c', d', e') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: (a, b, c, d, e) -> (a', b', c', d', e') Source #

(FromDyn a a', FromDyn b b', FromDyn c c', FromDyn d d', FromDyn e e', FromDyn f f') => FromDyn (a, b, c, d, e, f) (a', b', c', d', e', f') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: (a, b, c, d, e, f) -> (a', b', c', d', e', f') Source #

(FromDyn a a', FromDyn b b', FromDyn c c', FromDyn d d', FromDyn e e', FromDyn f f', FromDyn g g') => FromDyn (a, b, c, d, e, f, g) (a', b', c', d', e', f', g') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

fromDyn :: (a, b, c, d, e, f, g) -> (a', b', c', d', e', f', g') Source #

class ToDyn a b where Source #

ToDyn a b provides a function to convert type b to type a, where b is a type whose dynamic type occurences are replaced by concrete types.

For example: ToDyn (Int, Dynamic, Maybe Dynamic) (Int, Bool, Maybe String)

Minimal complete definition

Nothing

Methods

toDyn :: b -> a Source #

convert value of specified type to dynamic value

default toDyn :: (Generic a, Generic b, ToDyn' (Rep a) (Rep b)) => b -> a Source #

Instances

Instances details
ToDyn a a Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: a -> a Source #

Typeable a => ToDyn Dynamic a Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: a -> Dynamic Source #

(Typeable a, Show a) => ToDyn DynamicShow a Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: a -> DynamicShow Source #

ToDyn a b => ToDyn [a] [b] Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: [b] -> [a] Source #

ToDyn a b => ToDyn (Maybe a) (Maybe b) Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: Maybe b -> Maybe a Source #

(FromDyn a a', ToDyn b b') => ToDyn (a -> b) (a' -> b') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: (a' -> b') -> a -> b Source #

(ToDyn a a', ToDyn b b') => ToDyn (Either a b) (Either a' b') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: Either a' b' -> Either a b Source #

(ToDyn a a', ToDyn b b') => ToDyn (a, b) (a', b') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: (a', b') -> (a, b) Source #

(ToDyn a b, ToDyn c d) => ToDyn (a :* c) (b :* d) Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: (b :* d) -> a :* c Source #

(ToDyn a a', ToDyn b b', ToDyn c c') => ToDyn (a, b, c) (a', b', c') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: (a', b', c') -> (a, b, c) Source #

(ToDyn a a', ToDyn b b', ToDyn c c', ToDyn d d') => ToDyn (a, b, c, d) (a', b', c', d') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: (a', b', c', d') -> (a, b, c, d) Source #

(ToDyn a a', ToDyn b b', ToDyn c c', ToDyn d d', ToDyn e e') => ToDyn (a, b, c, d, e) (a', b', c', d', e') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: (a', b', c', d', e') -> (a, b, c, d, e) Source #

(ToDyn a a', ToDyn b b', ToDyn c c', ToDyn d d', ToDyn e e', ToDyn f f') => ToDyn (a, b, c, d, e, f) (a', b', c', d', e', f') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: (a', b', c', d', e', f') -> (a, b, c, d, e, f) Source #

(ToDyn a a', ToDyn b b', ToDyn c c', ToDyn d d', ToDyn e e', ToDyn f f', ToDyn g g') => ToDyn (a, b, c, d, e, f, g) (a', b', c', d', e', f', g') Source # 
Instance details

Defined in Test.Method.Dynamic

Methods

toDyn :: (a', b', c', d', e', f', g') -> (a, b, c, d, e, f, g) Source #

class Typeable (a :: k) #

The class Typeable allows a concrete representation of a type to be calculated.

Minimal complete definition

typeRep#

Monitor

Usage

Production code

type ExampleMethod env = Int -> String -> RIO env ()

class HasExampleMethod env where
  exampleL :: Lens' env (ExampleMethod env)

doit :: HasExampleMethod env => RIO env ()
doit = (do
  invoke exampleL 2 "foo"
  invoke exampleL 3 "foo"
  invoke exampleL (-1) "bar"
  invoke exampleL 3 "bar") catchAny (const $ pure ())

Test code

data Env = Env { _example :: ExampleMethod env }
makeLenses Env''

instance HasExampleMethod Env where
  exampleL = example

exampleMock :: ExampleMethod
exampleMock = mockup $ do
  when (args ((<0), anything)) `thenAction` throwString "negative n"
  when anything `thenReturn` ()

env = Env exampleMock

spec :: Spec
spec = describe "doit" $ do
  before $ withMonitor_ $ \monitor -> runRIO env $ local (exampleL %~ watch monitor) doit

  it "calls example _ \"foo\" twice" $ \logs -> do
    logs `shouldSatisfy` ((==2) `times` call (args (anything, (=="foo"))))

  it "calls example (-1) \"bar\" once" $ \logs -> do
    logs `shouldSatisfy` ((==1) `times` call (args ((==(-1)), (=="bar"))))

  it "does not call example 3 \"bar\" " $ \logs -> do
    logs `shouldSatisfy` ((==0) `times` call (args ((==3), (=="bar"))))

References

data Monitor args ret Source #

Monitor arg ret is an event monitor of methods, which logs method calls.

data Event args ret Source #

Event args ret is a function call event

Instances

Instances details
(Eq args, Eq ret) => Eq (Event args ret) Source # 
Instance details

Defined in Test.Method.Monitor.Internal

Methods

(==) :: Event args ret -> Event args ret -> Bool #

(/=) :: Event args ret -> Event args ret -> Bool #

(Ord args, Ord ret) => Ord (Event args ret) Source # 
Instance details

Defined in Test.Method.Monitor.Internal

Methods

compare :: Event args ret -> Event args ret -> Ordering #

(<) :: Event args ret -> Event args ret -> Bool #

(<=) :: Event args ret -> Event args ret -> Bool #

(>) :: Event args ret -> Event args ret -> Bool #

(>=) :: Event args ret -> Event args ret -> Bool #

max :: Event args ret -> Event args ret -> Event args ret #

min :: Event args ret -> Event args ret -> Event args ret #

(Show args, Show ret) => Show (Event args ret) Source # 
Instance details

Defined in Test.Method.Monitor.Internal

Methods

showsPrec :: Int -> Event args ret -> ShowS #

show :: Event args ret -> String #

showList :: [Event args ret] -> ShowS #

watchBy :: (Method method, MonadUnliftIO (Base method)) => (Args method -> args) -> (Ret method -> ret) -> Monitor args ret -> method -> method Source #

watchBy fArgs fRet monitor method decorates method so that monitor logs the method calls. This function is suited for monitoring multiple methods.

fArgs and fRet is converter for arguments/return values of given method.

foo :: Int -> IO String
foo = ...
bar :: Int -> String -> IO ()
bar = ...

data MonitorArgs = FooArgs Int | BarArgs (Int,String) deriving(Eq,Show)
data MonitorRet = FooRet String | BarRet () deriving(Eq, Show)

foo' :: Monitor MonitorArgs MonitorRet -> Int -> IO String
foo' monitor = watch monitor (FooArgs . toTuple) FooRet foo
bar' :: Monitor MonitorArgs MonitorRet -> Int -> String -> IO ()
bar' monitor = watch monitor (BarArgs . toTuple) BarRet bar

watch :: (Method method, MonadUnliftIO (Base method)) => Monitor (Args method) (Ret method) -> method -> method Source #

Simplified version of watchBy. It is suitable to monitor single method.

withMonitor :: MonadIO m => (Monitor args ret -> m a) -> m (a, [Event args ret]) Source #

withMonitor f calls f with Monitor, and then returns monitored event logs during the function call in addition to the return value of the function call

withMonitor_ :: MonadIO m => (Monitor args ret -> m ()) -> m [Event args ret] Source #

withMonitor_ f calls f with Monitor, and returns event logs during the call.

Matcher for events

call :: Matcher args -> Matcher (Event args ret) Source #

call matcher matches method call whose arguments matches matcher

times :: Matcher Int -> Matcher (Event args ret) -> Matcher [Event args ret] Source #

times countMatcher eventMatcher counts events that matches eventMatcher, and then the count matches countMatcher

Procedual api for monitor

newMonitor :: IO (Monitor args ret) Source #

Generate new instance of Monitor

listenEventLog :: MonadIO m => Monitor args ret -> m [Event args ret] Source #

Get current event logs from monitor

Protocol

Usage

Protocol is a DSL to write specification on communications between dependent methods. By using Protocol, you can specify

  • how many times each method is called,
  • what arguments are passed for each call, and
  • in which order methods are called.

For example, let's test user creation logic signup.

signup :: Service -> Username -> IO (Maybe UserId)
signup svc username = ...
type UserName = String
type UserId = Int

This method depends on Service, which consists of two methods.

  • findUser: checks whether the user name is taken already,
  • createUser: creates a user with given user name.
data Service = Service{
  findUser :: UserName -> IO (Maybe UserId),
  createUser :: UserName -> IO UserId
}

Let's check the following specification of signup method.

  1. If findUser returns Just user, it returns Nothing without calling createUser.
  2. If findUser returns Nothing, it calls createUser and returns the created user.

In order to write Protocol DSL, first call template haskell deriveLabel, which define a GADT functor that represents labels of dependent methods.

deriveLabel ''Service

This generates the following boilerplate.

data ServiceLabel m where
  FindUser :: ServiceLabel (UserName -> IO (Maybe UserId))
  CreateUser :: ServiceLabel (UserName -> IO UserId)

instance Label ServiceLabel where
  ...

Then, you can write test for the specification.

spec :: Spec
spec = do
  describe "signup" $ do
    let username = "user1"
        userId = 1
    context "if `findUser` returns `Just user`" $
      it "return `Nothing` without calling `createUser`" $ do
        -- Because env is stateful, it should be initialized for each test
        env <- protocol $ do
          decl $ whenArgs FindUser (==username) `thenReturn` Just userId
        -- mocking methods from protocol env. Each mock method raises an exception
        -- if it is called in a different way than that specified by the protocol.
        let service = mockInterface env
        signup service username `shouldReturn` Nothing
        -- Checks all calls specified by the protocol are called.
        verify env

      it "call `createUser` and return `Just userId`" $ do
        let proto = do
              findUserCall <- decl $ whenArgs FindUser (==username) `thenReturn` Nothing
              decl $ whenArgs CreateUser (==username) `thenReturn` Just userId `dependsOn` [findUserCall]
        -- withProtocol is easier API
        withProtocol proto $ \service ->
          signup service username `shouldReturn` Just userId

Protocol DSL consists of method call declarations like:

decl $ whenArgs FindUser (=="user1") `thenReturn` Nothing

This declaration specifies that findUser is called once with argument "user1" and it returns Nothing. If findUser is called with other argument, it raises an exception.

In protocol DSL, you can specify in which order methods are called, by using dependsOn function. For example:

findUserCall <- decl $ whenArgs FindUser (=="user1") `thenReturn` Nothing
decl $ whenArgs CreateUser (=="user1") `thenReturn` Nothing `dependsOn` [findUserCall]

findUser must be called before calling createUser. On the other hand, in the following example:

decl $ whenArgs FindUser (=="user1") `thenReturn` Nothing
decl $ whenArgs CreateUser (=="user1") `thenReturn` Nothing

the order of calling two methods does not matter.

However, each call declaration implicitly depends on the previous call declaration of the same method. For example:

decl $ whenArgs FindUser (=="user1") `thenReturn` Nothing
decl $ whenArgs FindUser (=="user2") `thenReturn` Just 1

findUser "user1" must be called before findUser "user2" is called.

References

protocol :: ProtocolM f a -> IO (ProtocolEnv f) Source #

Build ProtocolEnv from Protocol DSL.

withProtocol :: (Label f, MonadIO m) => ProtocolM f a -> (InterfaceOf f -> m b) -> m b Source #

withProtocol proto action executes action with a mock interface specified by proto, and then, it calls verify.

data ProtocolM f a Source #

Instances

Instances details
Monad (ProtocolM f) Source # 
Instance details

Defined in Test.Method.Protocol

Methods

(>>=) :: ProtocolM f a -> (a -> ProtocolM f b) -> ProtocolM f b #

(>>) :: ProtocolM f a -> ProtocolM f b -> ProtocolM f b #

return :: a -> ProtocolM f a #

Functor (ProtocolM f) Source # 
Instance details

Defined in Test.Method.Protocol

Methods

fmap :: (a -> b) -> ProtocolM f a -> ProtocolM f b #

(<$) :: a -> ProtocolM f b -> ProtocolM f a #

Applicative (ProtocolM f) Source # 
Instance details

Defined in Test.Method.Protocol

Methods

pure :: a -> ProtocolM f a #

(<*>) :: ProtocolM f (a -> b) -> ProtocolM f a -> ProtocolM f b #

liftA2 :: (a -> b -> c) -> ProtocolM f a -> ProtocolM f b -> ProtocolM f c #

(*>) :: ProtocolM f a -> ProtocolM f b -> ProtocolM f b #

(<*) :: ProtocolM f a -> ProtocolM f b -> ProtocolM f a #

data ProtocolEnv f Source #

ProtocolEnv f provides mock methods, where f is a GADT functor that represents the set of dependent methods.

data CallId Source #

Instances

Instances details
Eq CallId Source # 
Instance details

Defined in Test.Method.Protocol

Methods

(==) :: CallId -> CallId -> Bool #

(/=) :: CallId -> CallId -> Bool #

Ord CallId Source # 
Instance details

Defined in Test.Method.Protocol

Show CallId Source # 
Instance details

Defined in Test.Method.Protocol

deriveLabel :: Name -> DecsQ Source #

Generate the label type from given interface type.

  • Define GADT XXXLabel m for interface XXX.

    • FieldX :: XXXLabel X for each field fieldX :: X where X is a standard type.
    • PolyFieldX :: XXXLabel ty[Dynamic/a] for each field of the form polyFieldX :: (forall a. Typeable a => ty)

      • Type variable a is substituted with DynamicShow if a is instances of Show and Typeable
      • Type variable a is substituted with Dynamic if a is an instance of Typeable but not Show
      • Report an error if type variable a is not an instance of Typeable
  • Define instance Label XXXLabel.

Example

data API env = API {
    _foo :: Int -> RIO env Int,
    _bar :: forall a. (Show a, Typeable a) => String -> RIO env (Maybe a),
    _baz :: forall b. (Typeable a) => b -> RIO env ()
  }

deriveLabel ''API will generate the following code.

data APILabel env m where
    Foo :: APILabel env (Int -> RIO env Int)
    Bar :: APILabel env (String -> RIO env (Maybe DynamicShow)) -- type variable `a` is replaced with DynamicShow
    Baz :: APILabel env (Dynamic -> RIO env ()) -- type variable 'b' is replaced with Dynamic

instance Label (APILabel env) where
    type InterfaceOf (APILabel env) = API env
    toInterface k = API (k Foo) (castMethod (k Bar)) (castMethod (k Baz))
    showLabel x = case x of
      Foo -> Foo
      Bar -> Bar
      Baz -> Baz

data (f :|: g) a Source #

f :|: g is the disjoint union of label f and label g. Use this type when you want to specify a protocol for multiple interfaces.

Example

data FooService = FooService {
  foo :: Int -> IO Bool,
  ...
  }
data BarService = BarService {
  bar :: String -> IO (),
  ...
  }
deriveLabel ''FooService
deriveLabel ''BarService

proto :: ProtocolM (FooServiceLabel :|: BarServiceLabel) ()
proto = do
  i1 <- decl $ whenArgs (L Foo) (==1) `thenReturn` True
  void $ decl $ whenArgs (R Bar) (=="bar") `thenReturn` () `dependsOn` [i1]

main :: IO ()
main = withProtocol proto $ \(fooService, barService) -> do
  ...

Constructors

L (f a) 
R (g a) 

Instances

Instances details
(Label f, Label g) => Label (f :|: g) Source # 
Instance details

Defined in Test.Method.Label

Associated Types

type InterfaceOf (f :|: g) Source #

Methods

toInterface :: (forall m. (Typeable m, Method m, MonadIO (Base m), Show (Args m)) => (f :|: g) m -> m) -> InterfaceOf (f :|: g) Source #

showLabel :: (f :|: g) m -> String Source #

compareLabel :: (f :|: g) m1 -> (f :|: g) m2 -> Ordering Source #

(Eq (f a), Eq (g a)) => Eq ((f :|: g) a) Source # 
Instance details

Defined in Test.Method.Label

Methods

(==) :: (f :|: g) a -> (f :|: g) a -> Bool #

(/=) :: (f :|: g) a -> (f :|: g) a -> Bool #

(Ord (f a), Ord (g a)) => Ord ((f :|: g) a) Source # 
Instance details

Defined in Test.Method.Label

Methods

compare :: (f :|: g) a -> (f :|: g) a -> Ordering #

(<) :: (f :|: g) a -> (f :|: g) a -> Bool #

(<=) :: (f :|: g) a -> (f :|: g) a -> Bool #

(>) :: (f :|: g) a -> (f :|: g) a -> Bool #

(>=) :: (f :|: g) a -> (f :|: g) a -> Bool #

max :: (f :|: g) a -> (f :|: g) a -> (f :|: g) a #

min :: (f :|: g) a -> (f :|: g) a -> (f :|: g) a #

(Show (f a), Show (g a)) => Show ((f :|: g) a) Source # 
Instance details

Defined in Test.Method.Label

Methods

showsPrec :: Int -> (f :|: g) a -> ShowS #

show :: (f :|: g) a -> String #

showList :: [(f :|: g) a] -> ShowS #

type InterfaceOf (f :|: g) Source # 
Instance details

Defined in Test.Method.Label

decl :: Label f => Call f m -> ProtocolM f CallId Source #

Declare a method call specification. It returns the call id of the method call.

whenArgs :: ArgsMatcher (Args m) => f m -> EachMatcher (Args m) -> CallArgs f m Source #

Specify the argument condition of a method call

dependsOn :: Call f m -> [CallId] -> Call f m Source #

Specify on which method calls the call depends.

mockInterface :: Label f => ProtocolEnv f -> InterfaceOf f Source #

Get the mock interface from ProtocolEnv

lookupMock Source #

Arguments

:: forall f m. (Label f, Show (Args m), Method m, MonadIO (Base m)) 
=> f m

name of method

-> ProtocolEnv f 
-> m 

Get the mock method by method name. Return a unstubed method (which throws exception for every call) if the behavior of the method is unspecified by ProtocolEnv

lookupMockWithShow Source #

Arguments

:: forall f m. (Label f, Method m, MonadIO (Base m)) 
=> (Args m -> String)

show function for the argument of method

-> f m

name of method

-> ProtocolEnv f 
-> m 

Get the mock method by method name. Return a unstubed method (which throws exception for every call) if the behavior of the method is unspecified by ProtocolEnv. Use this function only if you want to customize show implementation for the argument of the method.

verify :: ProtocolEnv f -> IO () Source #

Verify that all method calls specified by Protocol DSL are fired.

Matcher

References

Basics

type Matcher a = a -> Bool Source #

anything :: Matcher a Source #

Matcher that matches anything

when :: Matcher a -> Matcher a Source #

synonym of id function. Use this function for improving readability

Matcher for method arguments

class TupleLike a where Source #

Associated Types

type AsTuple a Source #

Methods

fromTuple :: AsTuple a -> a Source #

toTuple :: a -> AsTuple a Source #

Instances

Instances details
TupleLike Nil Source # 
Instance details

Defined in Control.Method.Internal

Associated Types

type AsTuple Nil Source #

TupleLike (a :* (b :* (c :* (d :* (e :* (f :* (g :* Nil))))))) Source # 
Instance details

Defined in Control.Method.Internal

Associated Types

type AsTuple (a :* (b :* (c :* (d :* (e :* (f :* (g :* Nil))))))) Source #

Methods

fromTuple :: AsTuple (a :* (b :* (c :* (d :* (e :* (f :* (g :* Nil))))))) -> a :* (b :* (c :* (d :* (e :* (f :* (g :* Nil)))))) Source #

toTuple :: (a :* (b :* (c :* (d :* (e :* (f :* (g :* Nil))))))) -> AsTuple (a :* (b :* (c :* (d :* (e :* (f :* (g :* Nil))))))) Source #

TupleLike (a :* (b :* (c :* (d :* (e :* (f :* Nil)))))) Source # 
Instance details

Defined in Control.Method.Internal

Associated Types

type AsTuple (a :* (b :* (c :* (d :* (e :* (f :* Nil)))))) Source #

Methods

fromTuple :: AsTuple (a :* (b :* (c :* (d :* (e :* (f :* Nil)))))) -> a :* (b :* (c :* (d :* (e :* (f :* Nil))))) Source #

toTuple :: (a :* (b :* (c :* (d :* (e :* (f :* Nil)))))) -> AsTuple (a :* (b :* (c :* (d :* (e :* (f :* Nil)))))) Source #

TupleLike (a :* (b :* (c :* (d :* (e :* Nil))))) Source # 
Instance details

Defined in Control.Method.Internal

Associated Types

type AsTuple (a :* (b :* (c :* (d :* (e :* Nil))))) Source #

Methods

fromTuple :: AsTuple (a :* (b :* (c :* (d :* (e :* Nil))))) -> a :* (b :* (c :* (d :* (e :* Nil)))) Source #

toTuple :: (a :* (b :* (c :* (d :* (e :* Nil))))) -> AsTuple (a :* (b :* (c :* (d :* (e :* Nil))))) Source #

TupleLike (a :* (b :* (c :* (d :* Nil)))) Source # 
Instance details

Defined in Control.Method.Internal

Associated Types

type AsTuple (a :* (b :* (c :* (d :* Nil)))) Source #

Methods

fromTuple :: AsTuple (a :* (b :* (c :* (d :* Nil)))) -> a :* (b :* (c :* (d :* Nil))) Source #

toTuple :: (a :* (b :* (c :* (d :* Nil)))) -> AsTuple (a :* (b :* (c :* (d :* Nil)))) Source #

TupleLike (a :* (b :* (c :* Nil))) Source # 
Instance details

Defined in Control.Method.Internal

Associated Types

type AsTuple (a :* (b :* (c :* Nil))) Source #

Methods

fromTuple :: AsTuple (a :* (b :* (c :* Nil))) -> a :* (b :* (c :* Nil)) Source #

toTuple :: (a :* (b :* (c :* Nil))) -> AsTuple (a :* (b :* (c :* Nil))) Source #

TupleLike (a :* (b :* Nil)) Source # 
Instance details

Defined in Control.Method.Internal

Associated Types

type AsTuple (a :* (b :* Nil)) Source #

Methods

fromTuple :: AsTuple (a :* (b :* Nil)) -> a :* (b :* Nil) Source #

toTuple :: (a :* (b :* Nil)) -> AsTuple (a :* (b :* Nil)) Source #

TupleLike (a :* Nil) Source # 
Instance details

Defined in Control.Method.Internal

Associated Types

type AsTuple (a :* Nil) Source #

Methods

fromTuple :: AsTuple (a :* Nil) -> a :* Nil Source #

toTuple :: (a :* Nil) -> AsTuple (a :* Nil) Source #

class TupleLike a => ArgsMatcher a where Source #

Matcher for Args

>>> args ((==2), (>3)) (2 :* 4 :* Nil)
True
>>> args even (1 :* Nil)
False
>>> args () Nil
True

Methods

args :: EachMatcher a -> Matcher a Source #

Convert a tuple of matchers to a matcher of tuples

Instances

Instances details
ArgsMatcher Nil Source # 
Instance details

Defined in Test.Method.Matcher

Associated Types

type EachMatcher Nil Source #

ArgsMatcher (a :* (b :* (c :* (d :* (e :* (f :* (g :* Nil))))))) Source # 
Instance details

Defined in Test.Method.Matcher

Associated Types

type EachMatcher (a :* (b :* (c :* (d :* (e :* (f :* (g :* Nil))))))) Source #

Methods

args :: EachMatcher (a :* (b :* (c :* (d :* (e :* (f :* (g :* Nil))))))) -> Matcher (a :* (b :* (c :* (d :* (e :* (f :* (g :* Nil))))))) Source #

ArgsMatcher (a :* (b :* (c :* (d :* (e :* (f :* Nil)))))) Source # 
Instance details

Defined in Test.Method.Matcher

Associated Types

type EachMatcher (a :* (b :* (c :* (d :* (e :* (f :* Nil)))))) Source #

Methods

args :: EachMatcher (a :* (b :* (c :* (d :* (e :* (f :* Nil)))))) -> Matcher (a :* (b :* (c :* (d :* (e :* (f :* Nil)))))) Source #

ArgsMatcher (a :* (b :* (c :* (d :* (e :* Nil))))) Source # 
Instance details

Defined in Test.Method.Matcher

Associated Types

type EachMatcher (a :* (b :* (c :* (d :* (e :* Nil))))) Source #

Methods

args :: EachMatcher (a :* (b :* (c :* (d :* (e :* Nil))))) -> Matcher (a :* (b :* (c :* (d :* (e :* Nil))))) Source #

ArgsMatcher (a :* (b :* (c :* (d :* Nil)))) Source # 
Instance details

Defined in Test.Method.Matcher

Associated Types

type EachMatcher (a :* (b :* (c :* (d :* Nil)))) Source #

Methods

args :: EachMatcher (a :* (b :* (c :* (d :* Nil)))) -> Matcher (a :* (b :* (c :* (d :* Nil)))) Source #

ArgsMatcher (a :* (b :* (c :* Nil))) Source # 
Instance details

Defined in Test.Method.Matcher

Associated Types

type EachMatcher (a :* (b :* (c :* Nil))) Source #

Methods

args :: EachMatcher (a :* (b :* (c :* Nil))) -> Matcher (a :* (b :* (c :* Nil))) Source #

ArgsMatcher (a :* (b :* Nil)) Source # 
Instance details

Defined in Test.Method.Matcher

Associated Types

type EachMatcher (a :* (b :* Nil)) Source #

Methods

args :: EachMatcher (a :* (b :* Nil)) -> Matcher (a :* (b :* Nil)) Source #

ArgsMatcher (a :* Nil) Source # 
Instance details

Defined in Test.Method.Matcher

Associated Types

type EachMatcher (a :* Nil) Source #

Methods

args :: EachMatcher (a :* Nil) -> Matcher (a :* Nil) Source #

args' :: TupleLike a => Matcher (AsTuple a) -> Matcher a Source #

Convert a tuple matcher to a tuple-like matcher.

>>> args' (\(a, b) -> a * b == 10) (2 :* 5 :* Nil)
True
>>> args' (\(a, b) -> a * b == 10) (2 :* 4 :* Nil)
False