mockazo alternatives and similar packages
Based on the "Testing" category.
Alternatively, view mockazo alternatives based on common mentions on social networks and blogs.
-
hedgehog
Release with confidence, state-of-the-art property testing for Haskell. -
quickcheck-state-machine
Test monadic programs using state machine based models -
curl-runnings
A declarative test framework for quickly and easily writing integration tests against JSON API's. -
smallcheck
Test your Haskell code by exhaustively checking its properties -
ghc-prof-flamegraph
Generates data to be used with flamegraph.pl from .prof files. -
monad-mock
A Haskell package that provides a monad transformer for mocking mtl-style typeclasses -
test-framework
Framework for running and organising QuickCheck test properties and HUnit test cases -
fuzzcheck
A library for testing monadic code in the spirit of QuickCheck -
tasty-hedgehog
Tasty integration for the Hedgehog property testing library -
should-not-typecheck
A HUnit/hspec assertion to verify that an expression does not typecheck -
hspec-expectations-json
Hspec expectations on JSON Values -
quickcheck-arbitrary-adt
Typeclass for generating a list of each instance of a sum type's constructors -
hspec-golden-aeson
Use tests to monitor changes in Aeson serialization -
tasty-expected-failure
Mark test cases as expected-failure -
test-framework-th
Automagically (using Template Haskell) generates the Haskell-code you need when using HUnit -
test-framework-sandbox
test-sandbox support for the test-framework package -
markov-chain-usage-model
Computations for Markov chain usage models
Static code analysis for 29 languages.
Do you think we are missing an alternative of mockazo or a related project?
README
Mockazo ๐
Mock your records of functions with ease
One approach to structure a Haskell is using records of functions, sometimes called handles, services, or as we like to call them, components.
Mockazo provides a way of mocking components with ease and to verify that they executed the proper operations with the proper results.
Adding it to your project
Add the mockazo
dependency to your package.yaml
or your cabal
file.
If you use Stack, you also need to add multistate-0.8.0.2
to your extra-deps
section in the stack.yaml
file.
In your tests, import Data.Component.Mock
, and you are ready to roll!
Some restrictions
For Mockazo to work properly, we need that you do a little tweaking on your component definitions:
Parametrize the return context
It is common practice to make component methods return values in the IO
context.
This might looks straightforward, but when mocking comes into place, it is much easier
to work in other contexts.
Imagine that we have a simple logging component:
data Component = Component
{ logInfo :: Text -> IO ()
, logWarn :: Text -> IO ()
, logError :: Text -> IO ()
}
For Mockazo to work properly, we parametrize the context of execution:
data Component context = Component
{ logInfo :: Text -> context ()
, logWarn :: Text -> context ()
, logError :: Text -> context ()
}
This not only makes testing easier, but also makes your code much more robust,
because when defining a function that uses this component, we are unable to execute
any other kind of code that runs in another execution contexts (like a colleague
calling launchMissiles :: IO ()
).
All methods must return something in a context
Generally, we use Components to model pieces of our application that perform side effects, so adding a field that contains some static piece of data doesn't make much sense.
If you really need to do this, we recommend you that you create a companion Configuration
type, with all of these values, and leave the component for the side effect operations only.
If you really need the value inside of the component, wrap it in context
.
Mockazo, expects all the methods to be effectful. So it will choke on a return value that is not wrapped in the context.
So, instead of doing:
data Component context = Component
{ foo :: Text
}
Do this:
data Component context = Component
{ foo :: context Text
}
Creating your first mock
Let's suppose that we want to mock the logging component from the first example:
data Component context = Component
{ logInfo :: Text -> context ()
, logWarn :: Text -> context ()
, logError :: Text -> context ()
}
Create a separate module for the mock (we recommend you to do it in test/Mock
, and the
name of the module should match the name of the component module).
After that, we create the mock (following the advice, we make it in test/Mock/Logging.hs
):
module Mock.Logging where
import Logging -- We import the component *UNQUALIFIED*
makeMock ''Component
That's it! (Yes, really)
If for some reason, you want to add the export list to the mock module (your compiler is complaining), you can fix it like this:
module Mock.Logging (Action(..), Component(..), mock) where
import Logging
makeMock ''Component
Testing a function that calls our component
Suppose that somewhere we have a function importantOperation
that looks like this:
importantOperation :: Monad context => Logging.Component context -> context ()
importantOperation Logging.Component{..} = do
logInfo "info"
logWarn "warn"
logError "error"
We want to assure that these operations are run in order and with the appropriate arguments. We can write a test for it by using Mockazo's little DSL.
In our tests file:
import qualified Mock.Logging as Logging
let loggingMock = Logging.mock
-- ... somewhere in our test framework
runMock
$ withActions
[ Logging.LogInfo "info" :-> ()
, Logging.LogWarn "warn" :-> ()
, Logging.LogError "error" :-> ()
]
$ importantOperation loggingMock
We tell the test to run a function with a mock using runMock
.
After that, we specify the actions that we expect to be run, and what they return,
using the :->
operator, inside of a withActions
block.
Finally, we run the function that we want to test, by passing the mocked component to it.
Functions that depend on multiple components
Suppose that importantOperation
depended on two, three, or whatever more components.
The great stuff about Mockazo, is that you can chain as many withActions
blocks as
you want, passing the expected operations for each one of the mocks.
Suppose that apart from the Logging
component, we had another called UserFetch
.
We could also mock it in the same way we did with Logging
, and add the expected
operations too:
import qualified Mock.Logging as Logging
import qualified Mock.UserFetch as UserFetch
let loggingMock = Logging.mock
let userFetchMock = UserFetch.mock
-- ... somewhere our test framework
runMock
$ withActions
[ Logging.LogInfo "info" :-> ()
, Logging.LogWarn "warn" :-> ()
, Logging.LogError "error" :-> ()
]
$ withActions
[ UserFetch.Connect "localhost" :-> ()
, UserFetch.Fetch :-> User { name = "mike", password = "absolutely-encrypted" }
]
$ importantOperation loggingMock userFetchMock
Acknowledgements
Mockazo is heavily inspired by monad-mock
. It wouldn't have been possible to create this package without it's existence.
To all of the authors and contributors of monad-mock
:
Thank you!
*Note that all licence references and agreements mentioned in the mockazo README section above
are relevant to that project's source code only.