| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Test.HMock
Description
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:
classMonadm => MonadFilesystem m where readFile ::FilePath-> mStringwriteFile ::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.
Minimal complete definition
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.
Arguments
| :: (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 BoolShow instance to describe what
it is checking.
Predicates are used to define which arguments a general matcher should accept.
Constructors
| Predicate | |
Fields
| |
anything :: Predicate a Source #
A Predicate that accepts anything at all.
>>>accept anything "foo"True>>>accept anything undefinedTrue
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) 4False>>>accept (gt 5) 5False>>>accept (gt 5) 6True
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) 4False>>>accept (geq 5) 5True>>>accept (geq 5) 6True
lt :: (Show a, Ord a) => a -> Predicate a Source #
A Predicate that accepts anything less than the given value.
>>>accept (lt 5) 4True>>>accept (lt 5) 5False>>>accept (lt 5) 6False
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) 4True>>>accept (leq 5) 5True>>>accept (leq 5) 6False
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 Strings 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 Strings 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 matchesRegexmatchesCaseInsensitiveRegex.
>>>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 Strings 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 Strings 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 containsRegexcontainsCaseInsensitiveRegex.
>>>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 Strings 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 Predicates (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 |]) 3False>>>accept $(qIs [| even |]) 4True
>>>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 _) |]) NothingFalse>>>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 | |
| Num Multiplicity Source # | This is an incomplete instance, provided for convenience.
|
Defined in Test.HMock.Internal.Multiplicity Methods (+) :: 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 Methods 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 0False>>>meetsMultiplicity once 1True>>>meetsMultiplicity once 2False
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) 1False>>>meetsMultiplicity (atLeast 2) 2True>>>meetsMultiplicity (atLeast 2) 3True
atMost :: Multiplicity -> Multiplicity Source #
A Multiplicity that means at most this many times.
>>>meetsMultiplicity (atMost 2) 1True>>>meetsMultiplicity (atMost 2) 2True>>>meetsMultiplicity (atMost 2) 3False
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) 1False>>>meetsMultiplicity (between 2 3) 2True>>>meetsMultiplicity (between 2 3) 3True>>>meetsMultiplicity (between 2 3) 4False
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.
Associated Types
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.
Methods
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.
Minimal complete definition
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
Constructors
| 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.