Safe Haskell | None |
---|---|
Language | Haskell2010 |
This module provides a monad transformer, MockT
, which can be used to test
with mocks of Haskell mtl
-style type classes. To use a mock, you define
the expected actions and their results, and then run the code you are
testing. The framework verifies that the behavior of the code matched your
expectations.
For an introduction to the idea of mocks, see Mocks Aren't Stubs, by Martin Fowler.
WARNING: Hmock's API is likely to change soon. Please ensure you use an upper bound on the version number. The current API works fine for mocking with MTL-style classes. I want HMock to also work with effect systems, servant, haxl, and more. To accomplish this, I'll need to make breaking changes to the API.
Suppose you have a MonadFilesystem
typeclass, which is instantiated by
monads that implement filesystem operations:
classMonad
m => MonadFilesystem m where readFile ::FilePath
-> mString
writeFile ::FilePath
->String
-> m ()
You can use HMock to test code using MonadFilesystem
like this:
copyFile :: MonadFilesystem m =>FilePath
->FilePath
-> m () copyFile a b = readFile a >>= writeFile bmakeMockable
''MonadFilesystem spec = describe "copyFile"$
it "reads a file and writes its contents to another file"$
runMockT
$
doexpect
$
ReadFile "foo.txt"|->
"contents"expect
$
WriteFile "bar.txt" "contents"|->
() copyFile "foo.txt" "bar.txt"
The Template Haskell splice, makeMockable
, generates the
boilerplate needed to use MonadFilesystem
with HMock. You then use
runMockT
to begin a test with mocks, expect
to set up your expected
actions and responses, and finally execute your code.
Synopsis
- data MockT m a
- runMockT :: forall m a. MonadIO m => MockT m a -> m a
- withMockT :: forall m b. MonadIO m => ((forall a. MockT m a -> m a) -> MockT m b) -> m b
- describeExpectations :: MonadIO m => MockT m String
- verifyExpectations :: MonadIO m => MockT m ()
- byDefault :: forall cls name m r. (MonadIO m, MockableMethod cls name m r) => Rule cls name m r -> MockT m ()
- type MockableMethod (cls :: (Type -> Type) -> Constraint) (name :: Symbol) (m :: Type -> Type) (r :: Type) = (Mockable cls, Typeable m, KnownSymbol name, Typeable r)
- class Expectable cls name m r e | e -> cls name m r where
- data Rule (cls :: (Type -> Type) -> Constraint) (name :: Symbol) (m :: Type -> Type) (r :: Type)
- (|=>) :: Expectable cls name m r expectable => expectable -> (Action cls name m r -> MockT m r) -> Rule cls name m r
- (|->) :: (Monad m, Expectable cls name m r expectable) => expectable -> r -> Rule cls name m r
- class ExpectContext (t :: (Type -> Type) -> Type -> Type)
- expect :: (HasCallStack, MonadIO m, MockableMethod cls name m r, Expectable cls name m r expectable, ExpectContext ctx) => expectable -> ctx m ()
- expectN :: (HasCallStack, MonadIO m, MockableMethod cls name m r, Expectable cls name m r expectable, ExpectContext ctx) => Multiplicity -> expectable -> ctx m ()
- expectAny :: (HasCallStack, MonadIO m, MockableMethod cls name m r, Expectable cls name m r expectable, ExpectContext ctx) => expectable -> ctx m ()
- inSequence :: (MonadIO m, ExpectContext ctx) => (forall ctx'. ExpectContext ctx' => [ctx' m ()]) -> ctx m ()
- inAnyOrder :: (MonadIO m, ExpectContext ctx) => (forall ctx'. ExpectContext ctx' => [ctx' m ()]) -> ctx m ()
- anyOf :: (MonadIO m, ExpectContext ctx) => (forall ctx'. ExpectContext ctx' => [ctx' m ()]) -> ctx m ()
- times :: (MonadIO m, ExpectContext ctx) => Multiplicity -> (forall ctx'. ExpectContext ctx' => ctx' m ()) -> ctx m ()
- consecutiveTimes :: (MonadIO m, ExpectContext ctx) => Multiplicity -> (forall ctx'. ExpectContext ctx' => ctx' m ()) -> ctx m ()
- data Predicate a = Predicate {
- showPredicate :: String
- accept :: a -> Bool
- anything :: Predicate a
- eq :: (Show a, Eq a) => a -> Predicate a
- neq :: (Show a, Eq a) => a -> Predicate a
- gt :: (Show a, Ord a) => a -> Predicate a
- geq :: (Show a, Ord a) => a -> Predicate a
- lt :: (Show a, Ord a) => a -> Predicate a
- leq :: (Show a, Ord a) => a -> Predicate a
- just :: Predicate a -> Predicate (Maybe a)
- left :: Predicate a -> Predicate (Either a b)
- right :: Predicate b -> Predicate (Either a b)
- zipP :: Predicate a -> Predicate b -> Predicate (a, b)
- zip3P :: Predicate a -> Predicate b -> Predicate c -> Predicate (a, b, c)
- zip4P :: Predicate a -> Predicate b -> Predicate c -> Predicate d -> Predicate (a, b, c, d)
- zip5P :: Predicate a -> Predicate b -> Predicate c -> Predicate d -> Predicate e -> Predicate (a, b, c, d, e)
- andP :: Predicate a -> Predicate a -> Predicate a
- orP :: Predicate a -> Predicate a -> Predicate a
- notP :: Predicate a -> Predicate a
- startsWith :: (Show t, IsSequence t, Eq (Element t)) => t -> Predicate t
- endsWith :: (Show t, IsSequence t, Eq (Element t)) => t -> Predicate t
- hasSubstr :: (Show t, IsSequence t, Eq (Element t)) => t -> Predicate t
- hasSubsequence :: (Show t, IsSequence t, Eq (Element t)) => t -> Predicate t
- caseInsensitive :: (MonoFunctor t, MonoFunctor a, Element t ~ Char, Element a ~ Char) => (t -> Predicate a) -> t -> Predicate a
- matchesRegex :: (RegexLike Regex a, Eq a) => String -> Predicate a
- matchesCaseInsensitiveRegex :: (RegexLike Regex a, Eq a) => String -> Predicate a
- containsRegex :: (RegexLike Regex a, Eq a) => String -> Predicate a
- containsCaseInsensitiveRegex :: (RegexLike Regex a, Eq a) => String -> Predicate a
- isEmpty :: MonoFoldable t => Predicate t
- nonEmpty :: MonoFoldable t => Predicate t
- sizeIs :: MonoFoldable t => Predicate Int -> Predicate t
- elemsAre :: MonoFoldable t => [Predicate (Element t)] -> Predicate t
- unorderedElemsAre :: MonoFoldable t => [Predicate (Element t)] -> Predicate t
- each :: MonoFoldable t => Predicate (Element t) -> Predicate t
- contains :: MonoFoldable t => Predicate (Element t) -> Predicate t
- containsAll :: MonoFoldable t => [Predicate (Element t)] -> Predicate t
- containsOnly :: MonoFoldable t => [Predicate (Element t)] -> Predicate t
- containsKey :: (IsList t, Item t ~ (k, v)) => Predicate k -> Predicate t
- containsEntry :: (IsList t, Item t ~ (k, v)) => Predicate k -> Predicate v -> Predicate t
- keysAre :: (IsList t, Item t ~ (k, v)) => [Predicate k] -> Predicate t
- entriesAre :: (IsList t, Item t ~ (k, v)) => [(Predicate k, Predicate v)] -> Predicate t
- approxEq :: (RealFloat a, Show a) => a -> Predicate a
- finite :: RealFloat a => Predicate a
- infinite :: RealFloat a => Predicate a
- nAn :: RealFloat a => Predicate a
- is :: HasCallStack => (a -> Bool) -> Predicate a
- qIs :: HasCallStack => ExpQ -> ExpQ
- with :: HasCallStack => (a -> b) -> Predicate b -> Predicate a
- qWith :: ExpQ -> ExpQ
- qMatch :: PatQ -> ExpQ
- typed :: forall a b. (Typeable a, Typeable b) => Predicate a -> Predicate b
- data Multiplicity
- meetsMultiplicity :: Multiplicity -> Int -> Bool
- once :: Multiplicity
- anyMultiplicity :: Multiplicity
- atLeast :: Multiplicity -> Multiplicity
- atMost :: Multiplicity -> Multiplicity
- between :: Multiplicity -> Multiplicity -> Multiplicity
- class Typeable cls => MockableBase (cls :: (Type -> Type) -> Constraint) where
- data Action cls :: Symbol -> (Type -> Type) -> Type -> Type
- data Matcher cls :: Symbol -> (Type -> Type) -> Type -> Type
- showAction :: Action cls name m a -> String
- showMatcher :: Maybe (Action cls name m a) -> Matcher cls name m b -> String
- matchAction :: Matcher cls name m a -> Action cls name m a -> MatchResult
- class MockableBase cls => Mockable (cls :: (Type -> Type) -> Constraint) where
- setupMockable :: (MonadIO m, Typeable m) => proxy cls -> MockT m ()
- data MatchResult where
- NoMatch :: Int -> MatchResult
- Match :: MatchResult
- mockMethod :: forall cls name m r. (HasCallStack, MonadIO m, MockableMethod cls name m r, Default r) => Action cls name m r -> MockT m r
- mockDefaultlessMethod :: forall cls name m r. (HasCallStack, MonadIO m, MockableMethod cls name m r) => Action cls name m r -> MockT m r
Running mocks
Monad transformer for running mocks.
Instances
runMockT :: forall m a. MonadIO m => MockT m a -> m a Source #
Runs a test in the MockT
monad, handling all of the mocks.
withMockT :: forall m b. MonadIO m => ((forall a. MockT m a -> m a) -> MockT m b) -> m b Source #
Runs a test in the MockT
monad. The test can unlift other MockT pieces
to the base monad while still acting on the same set of expectations. This
can be useful for testing concurrency or similar mechanisms.
test =withMockT
$
inMockT -> doexpect
$
...liftIO
$
forkIO
$
inMockT firstThreadliftIO
$
forkIO
$
inMockT secondThread
This is a low-level primitive. Consider using the unliftio
package for
higher level implementations of multithreading and other primitives.
describeExpectations :: MonadIO m => MockT m String Source #
Fetches a String
that describes the current set of outstanding
expectations. This is sometimes useful for debugging test code. The exact
format is not specified.
verifyExpectations :: MonadIO m => MockT m () Source #
Verifies that all mock expectations are satisfied. You normally don't need
to do this, because it happens automatically at the end of your test in
runMockT
. However, it's occasionally useful to check expectations in the
middle of a test, such as before going on to the next stage.
Use of verifyExpectations
might signify that you are doing too much in a
single test. Consider splitting large tests into a separate test for each
case.
byDefault :: forall cls name m r. (MonadIO m, MockableMethod cls name m r) => Rule cls name m r -> MockT m () Source #
Changes the default response for matching actions.
Without byDefault
, actions with no explicit response will return the
Default
value for the type, or undefined
if the return type isn't an
instance of Default
. byDefault
replaces that with a new default
response, also overriding any previous defaults. The rule passed in must have
exactly one response.
Setting expectations
type MockableMethod (cls :: (Type -> Type) -> Constraint) (name :: Symbol) (m :: Type -> Type) (r :: Type) = (Mockable cls, Typeable m, KnownSymbol name, Typeable r) Source #
All constraints needed to mock a method with the given class, name, base monad, and return type.
class Expectable cls name m r e | e -> cls name m r where Source #
Something that can be expected. This type class covers a number of cases:
Instances
Expectable cls name m r (Matcher cls name m r) Source # | |
Expectable cls name m r (Rule cls name m r) Source # | |
data Rule (cls :: (Type -> Type) -> Constraint) (name :: Symbol) (m :: Type -> Type) (r :: Type) Source #
A rule for matching a method and responding to it when it matches.
The method may be matched by providing either an Action
to match exactly,
or a Matcher
. Exact matching is only available when all method arguments
A Rule
may have zero or more responses, which are attached using |->
and
|=>
. If there are no responses for a Rule
, then there must be a default
response for that action, and it is used. If more than one response is
added, the rule will perform the responses in order, repeating the last
response if there are additional matches.
Example:
expect
$ GetLine_anything
|->
"hello"|=>
(GetLine prompt) -> "The prompt was " ++ prompt|->
"quit"
Instances
Expectable cls name m r (Rule cls name m r) Source # | |
(|=>) :: Expectable cls name m r expectable => expectable -> (Action cls name m r -> MockT m r) -> Rule cls name m r infixl 1 Source #
Attaches a response to an expectation. This is a very flexible response,
which can look at arguments, do things in the base monad, set up more
expectations, etc. The matching Action
is passed to the response, and is
guaranteed to be a match so it's fine to just pattern match on the correct
method.
(|->) :: (Monad m, Expectable cls name m r expectable) => expectable -> r -> Rule cls name m r infixl 1 Source #
class ExpectContext (t :: (Type -> Type) -> Type -> Type) Source #
Type class for contexts in which it makes sense to express an expectation.
Notably, this includes MockT
, which expects actions to be performed during
a test.
Instances
ExpectContext MockT Source # | |
Defined in Test.HMock.Internal.MockT | |
ExpectContext Expected Source # | |
Defined in Test.HMock.Internal.Expectable |
expect :: (HasCallStack, MonadIO m, MockableMethod cls name m r, Expectable cls name m r expectable, ExpectContext ctx) => expectable -> ctx m () Source #
Creates an expectation that an action is performed once per given response (or exactly once if there is no response).
runMockT
$
doexpect
$
ReadFile "foo.txt"|->
"lorem ipsum"|->
"oops, the file changed out from under me!" callCodeUnderTest
In this example, readFile
must be called exactly twice by the tested code,
and will return "lorem ipsum" the first time, but something different the
second time.
:: (HasCallStack, MonadIO m, MockableMethod cls name m r, Expectable cls name m r expectable, ExpectContext ctx) | |
=> Multiplicity | The number of times the action should be performed. |
-> expectable | The action and its response. |
-> ctx m () |
expectAny :: (HasCallStack, MonadIO m, MockableMethod cls name m r, Expectable cls name m r expectable, ExpectContext ctx) => expectable -> ctx m () Source #
Specifies a response if a matching action is performed, but doesn't expect
anything. This is equivalent to
, but shorter.expectN
anyMultiplicity
In this example, the later use of whenever
overrides earlier uses, but only
for calls that match its conditions.
runMockT
$
dowhenever
$
ReadFile_ anything|->
"tlhIngan maH!"whenever
$
ReadFile "config.txt"|->
"lang: klingon" callCodeUnderTest
inSequence :: (MonadIO m, ExpectContext ctx) => (forall ctx'. ExpectContext ctx' => [ctx' m ()]) -> ctx m () Source #
Creates a sequential expectation. Other actions can still happen during the sequence, but these specific expectations must be met in this order.
inSequence
[expect
$
MoveForward,expect
$
TurnRight,expect
$
MoveForward ]
Beware of using inSequence
too often. It is appropriate when the property
you are testing is that the order of effects is correct. If that's not the
purpose of the test, consider adding several independent expectations,
instead. This avoids over-asserting, and keeps your tests less brittle.
inAnyOrder :: (MonadIO m, ExpectContext ctx) => (forall ctx'. ExpectContext ctx' => [ctx' m ()]) -> ctx m () Source #
Combines multiple expectations, which can occur in any order. Most of the time, you can achieve the same thing by expecting each separately, but this can be combined in complex expectations to describe more complex ordering constraints.
inSequence
[inAnyOrder
[expect
$
AdjustMirrors,expect
$
FastenSeatBelt ],expect
$
StartCar ]
anyOf :: (MonadIO m, ExpectContext ctx) => (forall ctx'. ExpectContext ctx' => [ctx' m ()]) -> ctx m () Source #
times :: (MonadIO m, ExpectContext ctx) => Multiplicity -> (forall ctx'. ExpectContext ctx' => ctx' m ()) -> ctx m () Source #
Creates a parent expectation that the child expectation will happen a
certain number of times. Unlike expectN
, the child expectation can be
arbitrarily complex and span multiple actions. Also unlike expectN
, each
new execution will restart response sequences for rules with more than one
response.
Different occurrences of the child can be interleaved. In case of ambiguity, progressing on an existing occurrence is preferred over starting a new occurrence.
consecutiveTimes :: (MonadIO m, ExpectContext ctx) => Multiplicity -> (forall ctx'. ExpectContext ctx' => ctx' m ()) -> ctx m () Source #
Creates a parent expectation that the child expectation will happen a
certain number of times. Unlike expectN
, the child expectation can be
arbitrarily complex and span multiple actions. Also unlike expectN
, each
new execution will restart response sequences for rules with more than one
response.
Different occurrences of the child must happen consecutively, with one finishing before the next begins.
Predicates
A predicate, which tests values and either accepts or rejects them. This
is similar to a ->
, but also has a Bool
Show
instance to describe what
it is checking.
Predicates are used to define which arguments a general matcher should accept.
Predicate | |
|
anything :: Predicate a Source #
A Predicate
that accepts anything at all.
>>>
accept anything "foo"
True>>>
accept anything undefined
True
eq :: (Show a, Eq a) => a -> Predicate a Source #
A Predicate
that accepts only the given value.
>>>
accept (eq "foo") "foo"
True>>>
accept (eq "foo") "bar"
False
neq :: (Show a, Eq a) => a -> Predicate a Source #
A Predicate
that accepts anything but the given value.
>>>
accept (neq "foo") "foo"
False>>>
accept (neq "foo") "bar"
True
gt :: (Show a, Ord a) => a -> Predicate a Source #
A Predicate
that accepts anything greater than the given value.
>>>
accept (gt 5) 4
False>>>
accept (gt 5) 5
False>>>
accept (gt 5) 6
True
geq :: (Show a, Ord a) => a -> Predicate a Source #
A Predicate
that accepts anything greater than or equal to the given
value.
>>>
accept (geq 5) 4
False>>>
accept (geq 5) 5
True>>>
accept (geq 5) 6
True
lt :: (Show a, Ord a) => a -> Predicate a Source #
A Predicate
that accepts anything less than the given value.
>>>
accept (lt 5) 4
True>>>
accept (lt 5) 5
False>>>
accept (lt 5) 6
False
leq :: (Show a, Ord a) => a -> Predicate a Source #
A Predicate
that accepts anything less than or equal to the given value.
>>>
accept (leq 5) 4
True>>>
accept (leq 5) 5
True>>>
accept (leq 5) 6
False
zipP :: Predicate a -> Predicate b -> Predicate (a, b) Source #
A Predicate
that accepts pairs whose elements satisfy the corresponding
child Predicates
.
>>>
accept (zipP (eq "foo") (eq "bar")) ("foo", "bar")
True>>>
accept (zipP (eq "foo") (eq "bar")) ("bar", "foo")
False
zip4P :: Predicate a -> Predicate b -> Predicate c -> Predicate d -> Predicate (a, b, c, d) Source #
A Predicate
that accepts 3-tuples whose elements satisfy the
corresponding child Predicates
.
>>>
accept (zip4P (eq 1) (eq 2) (eq 3) (eq 4)) (1, 2, 3, 4)
True>>>
accept (zip4P (eq 1) (eq 2) (eq 3) (eq 4)) (4, 3, 2, 1)
False
zip5P :: Predicate a -> Predicate b -> Predicate c -> Predicate d -> Predicate e -> Predicate (a, b, c, d, e) Source #
A Predicate
that accepts 3-tuples whose elements satisfy the
corresponding child Predicates
.
>>>
accept (zip5P (eq 1) (eq 2) (eq 3) (eq 4) (eq 5)) (1, 2, 3, 4, 5)
True>>>
accept (zip5P (eq 1) (eq 2) (eq 3) (eq 4) (eq 5)) (5, 4, 3, 2, 1)
False
andP :: Predicate a -> Predicate a -> Predicate a Source #
A Predicate
that accepts anything accepted by both of its children.
>>>
accept (lt "foo" `andP` gt "bar") "eta"
True>>>
accept (lt "foo" `andP` gt "bar") "quz"
False>>>
accept (lt "foo" `andP` gt "bar") "alpha"
False
orP :: Predicate a -> Predicate a -> Predicate a Source #
A Predicate
that accepts anything accepted by either of its children.
>>>
accept (lt "bar" `orP` gt "foo") "eta"
False>>>
accept (lt "bar" `orP` gt "foo") "quz"
True>>>
accept (lt "bar" `orP` gt "foo") "alpha"
True
startsWith :: (Show t, IsSequence t, Eq (Element t)) => t -> Predicate t Source #
A Predicate
that accepts sequences that start with the given prefix.
>>>
accept (startsWith "fun") "fungible"
True>>>
accept (startsWith "gib") "fungible"
False
endsWith :: (Show t, IsSequence t, Eq (Element t)) => t -> Predicate t Source #
A Predicate
that accepts sequences that end with the given suffix.
>>>
accept (endsWith "ow") "crossbow"
True>>>
accept (endsWith "ow") "trebuchet"
False
hasSubstr :: (Show t, IsSequence t, Eq (Element t)) => t -> Predicate t Source #
A Predicate
that accepts sequences that contain the given (consecutive)
substring.
>>>
accept (hasSubstr "i") "team"
False>>>
accept (hasSubstr "i") "partnership"
True
hasSubsequence :: (Show t, IsSequence t, Eq (Element t)) => t -> Predicate t Source #
A Predicate
that accepts sequences that contain the given (not
necessarily consecutive) subsequence.
>>>
accept (hasSubsequence [1..5]) [1, 2, 3, 4, 5]
True>>>
accept (hasSubsequence [1..5]) [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0]
True>>>
accept (hasSubsequence [1..5]) [2, 3, 5, 7, 11]
False
caseInsensitive :: (MonoFunctor t, MonoFunctor a, Element t ~ Char, Element a ~ Char) => (t -> Predicate a) -> t -> Predicate a Source #
Transforms a Predicate
on String
s or string-like types to match without
regard to case.
>>>
accept (caseInsensitive startsWith "foo") "FOOTBALL!"
True>>>
accept (caseInsensitive endsWith "ball") "soccer"
False>>>
accept (caseInsensitive eq "time") "TIME"
True>>>
accept (caseInsensitive gt "NOTHING") "everything"
False
matchesRegex :: (RegexLike Regex a, Eq a) => String -> Predicate a Source #
A Predicate
that accepts String
s or string-like values matching a
regular expression. The expression must match the entire argument.
You should not use
, because regular
expression syntax itself is still case-sensitive even when the text you are
matching is not. Instead, use caseInsensitive
matchesRegex
matchesCaseInsensitiveRegex
.
>>>
accept (matchesRegex "x{2,5}y?") "xxxy"
True>>>
accept (matchesRegex "x{2,5}y?") "xyy"
False>>>
accept (matchesRegex "x{2,5}y?") "wxxxyz"
False
matchesCaseInsensitiveRegex :: (RegexLike Regex a, Eq a) => String -> Predicate a Source #
A Predicate
that accepts String
s or string-like values matching a
regular expression in a case-insensitive way. The expression must match the
entire argument.
You should use this instead of
, because
regular expression syntax itself is still case-sensitive even when the text
you are matching is not.caseInsensitive
matchesRegex
>>>
accept (matchesCaseInsensitiveRegex "x{2,5}y?") "XXXY"
True>>>
accept (matchesCaseInsensitiveRegex "x{2,5}y?") "XYY"
False>>>
accept (matchesCaseInsensitiveRegex "x{2,5}y?") "WXXXYZ"
False
containsRegex :: (RegexLike Regex a, Eq a) => String -> Predicate a Source #
A Predicate
that accepts String
s or string-like values containing a
match for a regular expression. The expression need not match the entire
argument.
You should not use
, because regular
expression syntax itself is still case-sensitive even when the text you are
matching is not. Instead, use caseInsensitive
containsRegex
containsCaseInsensitiveRegex
.
>>>
accept (containsRegex "x{2,5}y?") "xxxy"
True>>>
accept (containsRegex "x{2,5}y?") "xyy"
False>>>
accept (containsRegex "x{2,5}y?") "wxxxyz"
True
containsCaseInsensitiveRegex :: (RegexLike Regex a, Eq a) => String -> Predicate a Source #
A Predicate
that accepts String
s or string-like values containing a
match for a regular expression in a case-insensitive way. The expression
need match the entire argument.
You should use this instead of
, because
regular expression syntax itself is still case-sensitive even when the text
you are matching is not.caseInsensitive
containsRegex
>>>
accept (containsCaseInsensitiveRegex "x{2,5}y?") "XXXY"
True>>>
accept (containsCaseInsensitiveRegex "x{2,5}y?") "XYY"
False>>>
accept (containsCaseInsensitiveRegex "x{2,5}y?") "WXXXYZ"
True
isEmpty :: MonoFoldable t => Predicate t Source #
A Predicate
that accepts empty data structures.
>>>
accept isEmpty []
True>>>
accept isEmpty [1, 2, 3]
False>>>
accept isEmpty ""
True>>>
accept isEmpty "gas tank"
False
nonEmpty :: MonoFoldable t => Predicate t Source #
A Predicate
that accepts non-empty data structures.
>>>
accept nonEmpty []
False>>>
accept nonEmpty [1, 2, 3]
True>>>
accept nonEmpty ""
False>>>
accept nonEmpty "gas tank"
True
elemsAre :: MonoFoldable t => [Predicate (Element t)] -> Predicate t Source #
A Predicate
that accepts data structures whose contents each match the
corresponding Predicate
in the given list, in the same order.
>>>
accept (elemsAre [lt 3, lt 4, lt 5]) [2, 3, 4]
True>>>
accept (elemsAre [lt 3, lt 4, lt 5]) [2, 3, 4, 5]
False>>>
accept (elemsAre [lt 3, lt 4, lt 5]) [2, 10, 4]
False
unorderedElemsAre :: MonoFoldable t => [Predicate (Element t)] -> Predicate t Source #
A Predicate
that accepts data structures whose contents each match the
corresponding Predicate
in the given list, in any order.
>>>
accept (unorderedElemsAre [eq 1, eq 2, eq 3]) [1, 2, 3]
True>>>
accept (unorderedElemsAre [eq 1, eq 2, eq 3]) [2, 3, 1]
True>>>
accept (unorderedElemsAre [eq 1, eq 2, eq 3]) [1, 2, 3, 4]
False>>>
accept (unorderedElemsAre [eq 1, eq 2, eq 3]) [1, 3]
False
containsAll :: MonoFoldable t => [Predicate (Element t)] -> Predicate t Source #
A Predicate
that accepts data structures which contain an element
satisfying each of the child predicates.
is equivalent to containsAll
[p1, p2, ..., pn]
.contains
p1 `andP
` contains
p2 `andP
` ... `andP
`
contains
pn
>>>
accept (containsAll [eq "foo", eq "bar"]) ["bar", "foo"]
True>>>
accept (containsAll [eq "foo", eq "bar"]) ["foo"]
False>>>
accept (containsAll [eq "foo", eq "bar"]) ["foo", "bar", "qux"]
True
containsOnly :: MonoFoldable t => [Predicate (Element t)] -> Predicate t Source #
A Predicate
that accepts data structures whose elements all satisfy at
least one of the child predicates.
is
equivalent to containsOnly
[p1, p2, ..., pn]
.each
(p1 `orP
` p2 `orP
` ... `orP
` pn)
>>>
accept (containsOnly [eq "foo", eq "bar"]) ["foo", "foo"]
True>>>
accept (containsOnly [eq "foo", eq "bar"]) ["foo", "bar"]
True>>>
accept (containsOnly [eq "foo", eq "bar"]) ["foo", "qux"]
False
containsEntry :: (IsList t, Item t ~ (k, v)) => Predicate k -> Predicate v -> Predicate t Source #
A Predicate
that accepts map-like structures which contain a key/value
pair matched by the given child Predicate
s (one for the key, and one for
the value).
>>>
accept (containsEntry (eq "foo") (gt 10)) [("foo", 12), ("bar", 5)]
True>>>
accept (containsEntry (eq "foo") (gt 10)) [("foo", 5), ("bar", 12)]
False>>>
accept (containsEntry (eq "foo") (gt 10)) [("bar", 12)]
False
keysAre :: (IsList t, Item t ~ (k, v)) => [Predicate k] -> Predicate t Source #
A Predicate
that accepts map-like structures whose keys are exactly those
matched by the given list of Predicates
, in any order.
>>>
accept (keysAre [eq "a", eq "b", eq "c"]) [("a", 1), ("b", 2), ("c", 3)]
True>>>
accept (keysAre [eq "a", eq "b", eq "c"]) [("c", 1), ("b", 2), ("a", 3)]
True>>>
accept (keysAre [eq "a", eq "b", eq "c"]) [("a", 1), ("c", 3)]
False>>>
accept (keysAre [eq "a", eq "b"]) [("a", 1), ("b", 2), ("c", 3)]
False
entriesAre :: (IsList t, Item t ~ (k, v)) => [(Predicate k, Predicate v)] -> Predicate t Source #
A Predicate
that accepts map-like structures whose entries are exactly
those matched by the given list of Predicate
pairs, in any order.
>>>
accept (entriesAre [(eq 1, eq 2), (eq 3, eq 4)]) [(1, 2), (3, 4)]
True>>>
accept (entriesAre [(eq 1, eq 2), (eq 3, eq 4)]) [(3, 4), (1, 2)]
True>>>
accept (entriesAre [(eq 1, eq 2), (eq 3, eq 4)]) [(1, 4), (3, 2)]
False>>>
accept (entriesAre [(eq 1, eq 2), (eq 3, eq 4)]) [(1, 2), (3, 4), (5, 6)]
False
approxEq :: (RealFloat a, Show a) => a -> Predicate a Source #
A Predicate
that accepts values of RealFloat
types that are close to
the given number. The expected precision is scaled based on the target
value, so that reasonable rounding error is accepted but grossly inaccurate
results are not.
The following naive use of eq
fails due to rounding:
>>>
accept (eq 1.0) (sum (replicate 100 0.01))
False
The solution is to use approxEq
, which accounts for rounding error.
However, approxEq
doesn't accept results that are far enough off that they
likely arise from incorrect calculations instead of rounding error.
>>>
accept (approxEq 1.0) (sum (replicate 100 0.01))
True>>>
accept (approxEq 1.0) (sum (replicate 100 0.009999))
False
qIs :: HasCallStack => ExpQ -> ExpQ Source #
A Template Haskell splice that acts like is
, but receives a quoted
expression at compile time and has a more helpful description for error
messages.
>>>
accept $(qIs [| even |]) 3
False>>>
accept $(qIs [| even |]) 4
True
>>>
show $(qIs [| even |])
"even"
with :: HasCallStack => (a -> b) -> Predicate b -> Predicate a Source #
A combinator to lift a Predicate
to work on a property or computed value
of the original value.
>>>
accept (with abs (gt 5)) (-6)
True>>>
accept (with abs (gt 5)) (-5)
False>>>
accept (with reverse (eq "olleh")) "hello"
True>>>
accept (with reverse (eq "olleh")) "goodbye"
False
qWith :: ExpQ -> ExpQ Source #
A Template Haskell splice that acts like is
, but receives a quoted typed
expression at compile time and has a more helpful description for error
messages.
>>>
accept ($(qWith [| abs |]) (gt 5)) (-6)
True>>>
accept ($(qWith [| abs |]) (gt 5)) (-5)
False>>>
accept ($(qWith [| reverse |]) (eq "olleh")) "hello"
True>>>
accept ($(qWith [| reverse |]) (eq "olleh")) "goodbye"
False
>>>
show ($(qWith [| abs |]) (gt 5))
"abs: > 5"
qMatch :: PatQ -> ExpQ Source #
A Template Haskell splice that turns a quoted pattern into a predicate that accepts values that match the pattern.
>>>
accept $(qMatch [p| Just (Left _) |]) Nothing
False>>>
accept $(qMatch [p| Just (Left _) |]) (Just (Left 5))
True>>>
accept $(qMatch [p| Just (Left _) |]) (Just (Right 5))
False
>>>
show $(qMatch [p| Just (Left _) |])
"Just (Left _)"
typed :: forall a b. (Typeable a, Typeable b) => Predicate a -> Predicate b Source #
Converts a Predicate
to a new type. Typically used with visible type
application, as in the examples below.
>>>
accept (typed @String anything) "foo"
True>>>
accept (typed @String (sizeIs (gt 5))) "foo"
False>>>
accept (typed @String anything) (42 :: Int)
False
Multiplicity
data Multiplicity Source #
An acceptable range of number of times for something to happen.
A multiplicity can have a lower and an upper bound.
Instances
Eq Multiplicity Source # | |
Defined in Test.HMock.Internal.Multiplicity (==) :: Multiplicity -> Multiplicity -> Bool # (/=) :: Multiplicity -> Multiplicity -> Bool # | |
Num Multiplicity Source # | This is an incomplete instance, provided for convenience.
|
Defined in Test.HMock.Internal.Multiplicity (+) :: Multiplicity -> Multiplicity -> Multiplicity # (-) :: Multiplicity -> Multiplicity -> Multiplicity # (*) :: Multiplicity -> Multiplicity -> Multiplicity # negate :: Multiplicity -> Multiplicity # abs :: Multiplicity -> Multiplicity # signum :: Multiplicity -> Multiplicity # fromInteger :: Integer -> Multiplicity # | |
Show Multiplicity Source # | |
Defined in Test.HMock.Internal.Multiplicity showsPrec :: Int -> Multiplicity -> ShowS # show :: Multiplicity -> String # showList :: [Multiplicity] -> ShowS # |
meetsMultiplicity :: Multiplicity -> Int -> Bool Source #
Checks whether a certain number satisfies the Multiplicity
.
once :: Multiplicity Source #
A Multiplicity
that means exactly once.
>>>
meetsMultiplicity once 0
False>>>
meetsMultiplicity once 1
True>>>
meetsMultiplicity once 2
False
anyMultiplicity :: Multiplicity Source #
A Multiplicity
that means any number of times.
>>> meetsMultiplicity anyMultiplicity 0
True
>>> meetsMultiplicity anyMultiplicity 1
True
>>> meetsMultiplicity anyMultiplicity 10
True
atLeast :: Multiplicity -> Multiplicity Source #
A Multiplicity
that means at least this many times.
>>>
meetsMultiplicity (atLeast 2) 1
False>>>
meetsMultiplicity (atLeast 2) 2
True>>>
meetsMultiplicity (atLeast 2) 3
True
atMost :: Multiplicity -> Multiplicity Source #
A Multiplicity
that means at most this many times.
>>>
meetsMultiplicity (atMost 2) 1
True>>>
meetsMultiplicity (atMost 2) 2
True>>>
meetsMultiplicity (atMost 2) 3
False
between :: Multiplicity -> Multiplicity -> Multiplicity Source #
A Multiplicity
that means any number in this interval, endpoints
included. For example,
means 2 or 3 times, while
between
2 3
is equivalent to between
n nn
.
>>>
meetsMultiplicity (between 2 3) 1
False>>>
meetsMultiplicity (between 2 3) 2
True>>>
meetsMultiplicity (between 2 3) 3
True>>>
meetsMultiplicity (between 2 3) 4
False
Implementing mocks
class Typeable cls => MockableBase (cls :: (Type -> Type) -> Constraint) where Source #
A base class for Monad
subclasses whose methods can be mocked. You
usually want to generate this instance using makeMockable
,
makeMockableBase
, deriveMockable
, or
deriveMockableBase
. It's just boilerplate.
data Action cls :: Symbol -> (Type -> Type) -> Type -> Type Source #
An action that is performed. This data type will have one constructor for each method.
data Matcher cls :: Symbol -> (Type -> Type) -> Type -> Type Source #
A specification for matching actions. The actual arguments should be replaced with predicates.
showAction :: Action cls name m a -> String Source #
Gets a text description of an Action
, for use in error messages.
showMatcher :: Maybe (Action cls name m a) -> Matcher cls name m b -> String Source #
Gets a text description of a Matcher
, for use in error messages.
matchAction :: Matcher cls name m a -> Action cls name m a -> MatchResult Source #
class MockableBase cls => Mockable (cls :: (Type -> Type) -> Constraint) where Source #
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.
Nothing
data MatchResult where Source #
The result of matching a
with an Matcher
a
. Because the
types should already guarantee that the methods match, all that's left is to
match arguments.Action
b
NoMatch :: Int -> MatchResult | No match. The int is the number of arguments that don't match. |
Match :: MatchResult | Match. Stores a witness to the equality of return types. |
mockMethod :: forall cls name m r. (HasCallStack, MonadIO m, MockableMethod cls name m r, Default r) => Action cls name m r -> MockT m r Source #
Implements a method in a Mockable
monad by delegating to the mock
framework. If the method is called unexpectedly, an exception will be
thrown. However, an expected invocation without a specified response will
return the default value.
mockDefaultlessMethod :: forall cls name m r. (HasCallStack, MonadIO m, MockableMethod cls name m r) => Action cls name m r -> MockT m r Source #
Implements a method in a Mockable
monad by delegating to the mock
framework. If the method is called unexpectedly, an exception will be
thrown. However, an expected invocation without a specified response will
return undefined. This can be used in place of mockMethod
when the return
type has no default.