diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,25 @@
+The MIT License (MIT)
+=====================
+
+Copyright © `2019` `The Agile Monkeys`
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the “Software”), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,205 @@
+# Mockazo 👃
+_Mock your records of functions with ease_
+
+[![CircleCI](https://circleci.com/gh/theam/mockazo.svg?style=svg&circle-token=e80d08cb0d9855a774709311335f4e29ca40f5de)](https://circleci.com/gh/theam/mockazo)
+[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
+[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/theam/mockazo.svg)](http://isitmaintained.com/project/theam/mockazo "Average time to resolve an issue")
+[![GitHub license](https://img.shields.io/github/license/theam/mockazo.svg)](https://github.com/theam/mockazo/blob/master/LICENSE)
+[![Hackage version](https://img.shields.io/hackage/v/mockazo.svg)](https://hackage.haskell.org/package/mockazo)
+[![Open Source Love png1](https://badges.frapsoft.com/os/v1/open-source.png?v=103)](https://github.com/ellerbrock/open-source-badges/)
+
+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:
+
+```haskell
+data Component = Component
+  { logInfo  :: Text -> IO ()
+  , logWarn  :: Text -> IO ()
+  , logError :: Text -> IO ()
+  }
+```
+
+For Mockazo to work properly, we parametrize the context of execution:
+
+```haskell
+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:
+
+```haskell
+data Component context = Component
+  { foo :: Text
+  }
+```
+
+Do this:
+
+```haskell
+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:
+
+```haskell
+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`):
+
+```haskell
+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:
+
+```haskell
+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:
+
+```haskell
+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:
+
+```haskell
+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:
+
+```haskell
+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`](https://github.com/cjdev/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!**
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/mockazo.cabal b/mockazo.cabal
new file mode 100644
--- /dev/null
+++ b/mockazo.cabal
@@ -0,0 +1,71 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.31.1.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 835e8ef5d2e80d1f671e85ac3cee6fc223d5a0055d244bf702a12ea687d50cd7
+
+name:           mockazo
+version:        0.1.0
+synopsis:       Mock records of functions easily
+description:    Please see the README on GitHub at <https://github.com/theam/mockazo#readme>
+category:       Testing
+homepage:       https://github.com/theam/mockazo#readme
+bug-reports:    https://github.com/theam/mockazo/issues
+author:         The Agile Monkeys
+maintainer:     hackers@theagilemonkeys.com
+copyright:      2019 The Agile Monkeys
+license:        MIT
+license-file:   LICENSE.md
+build-type:     Simple
+extra-source-files:
+    README.md
+    LICENSE.md
+
+source-repository head
+  type: git
+  location: https://github.com/theam/mockazo
+
+library
+  exposed-modules:
+      Data.Component.Mock
+  other-modules:
+      Data.Component.Mock.TH
+      Data.Component.Mock.TH.Common
+      Data.Component.Mock.TH.Gadt
+      Data.Component.Mock.TH.Instance
+      Data.Component.Mock.TH.NewFunction
+      Paths_mockazo
+  hs-source-dirs:
+      src
+  default-extensions: ConstraintKinds DataKinds DefaultSignatures FlexibleContexts GADTs KindSignatures NoImplicitPrelude OverloadedStrings RankNTypes RecordWildCards StandaloneDeriving ScopedTypeVariables TemplateHaskell TupleSections TypeOperators
+  ghc-options: -Wall -fno-warn-orphans -fno-warn-name-shadowing -optP-Wno-nonportable-include-path -Wincomplete-uni-patterns -Wincomplete-record-updates -Wcompat -Widentities -Wredundant-constraints -Wmissing-export-lists -Wpartial-fields -fhide-source-paths -freverse-errors
+  build-depends:
+      base >=4.7 && <5
+    , constraints >=0.10
+    , multistate >=0.8
+    , relude >=0.4
+    , template-haskell >=2.14
+  default-language: Haskell2010
+
+test-suite mockazo-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Mock.HigherKinded
+      Mock.SingleKinded
+      Paths_mockazo
+  hs-source-dirs:
+      test
+  default-extensions: ConstraintKinds DataKinds DefaultSignatures FlexibleContexts GADTs KindSignatures NoImplicitPrelude OverloadedStrings RankNTypes RecordWildCards StandaloneDeriving ScopedTypeVariables TemplateHaskell TupleSections TypeOperators
+  ghc-options: -Wall -fno-warn-orphans -fno-warn-name-shadowing -optP-Wno-nonportable-include-path -Wincomplete-uni-patterns -Wincomplete-record-updates -Wcompat -Widentities -Wredundant-constraints -Wmissing-export-lists -Wpartial-fields -fhide-source-paths -freverse-errors -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.7 && <5
+    , constraints >=0.10
+    , hspec
+    , mockazo
+    , multistate >=0.8
+    , relude >=0.4
+    , template-haskell >=2.14
+  default-language: Haskell2010
diff --git a/src/Data/Component/Mock.hs b/src/Data/Component/Mock.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Component/Mock.hs
@@ -0,0 +1,112 @@
+module Data.Component.Mock
+  ( (:~:)(..)
+  , WithResult(..)
+  , IsAction(..)
+  , mockAction
+  , InContextOf
+  , Executes
+  , runMock
+  , withActions
+
+  , module Data.Component.Mock.TH
+  ) where
+
+import Relude
+import Data.Type.Equality ((:~:)(..))
+import Data.Constraint ((:-), (\\))
+import Data.Constraint.Forall (ForallF, instF)
+import Control.Monad.Trans.MultiState hiding (MultiState)
+import Data.HList.ContainsType
+
+import Data.Component.Mock.TH
+
+{- | Context that gets injected to the components
+to store the values that are being executed.
+
+These actions will be compared during the tests
+with the ones that you expect, failing in case
+of a mismatch.
+-}
+type InContextOf s =
+  MultiStateT s IO
+
+{-| Constraint to specify that some actions
+contain the type of actions to be executed.
+-}
+type Executes action actions =
+  ContainsType [WithResult action] actions
+
+{-| Runs the mock context and all the checks
+with it
+-}
+runMock :: InContextOf '[] a -> IO ()
+runMock = runMultiStateTNil_
+
+{-| Specify the expected actions for a given component
+to expect to be executed
+-}
+withActions
+  :: IsAction action
+  => [WithResult action]
+  -> InContextOf ([WithResult action] : otherActions) a
+  -> InContextOf otherActions (a, [WithResult action])
+withActions actions execution = do
+  (result, actionsRest) <- withMultiState actions execution
+  case actionsRest of
+    [] ->
+      pure (result, actionsRest)
+
+    remainingActions ->
+      error
+        $ "Execution ended, but those actions were expected to be run:\n"
+        <> unlines (fmap (\(action :-> _) -> "  • '" <> showAction action <> "'") remainingActions)
+
+{-| Operator to specify that an action returns some result
+-}
+data WithResult action where
+  (:->) :: action result -> result -> WithResult action
+
+{-| Class that all actions must implement in order to work
+with the rest of the library.
+
+You only need to implement 'eqAction'
+-}
+class IsAction (action :: Type -> Type) where
+  eqAction :: action a -> action b -> Maybe (a :~: b)
+  showAction :: action a -> Text
+
+  default showAction :: ForallF Show action => action a -> Text
+  showAction =
+    toText . showAction'
+   where
+    showAction' :: forall g a. ForallF Show g => g a -> String
+    showAction' x = show x \\ (instF :: ForallF Show g :- Show (g a))
+
+{-| Utility function to be used in the creation of the mock
+components, so the methods store action values instead of
+executing anything else
+-}
+mockAction
+  :: ContainsType ([WithResult action]) actions
+  => IsAction action
+  => Text
+  -> action result
+  -> InContextOf actions result
+mockAction functionName action = do
+  nextAction <- mGet
+  case nextAction of
+    [] ->
+      error
+        $ "Expected end of program, but called '" <> functionName <> "'\n"
+        <> "  given action: '" <> showAction action <> "'\n"
+
+    (action' :-> result) : actions
+     | Just Refl <- action `eqAction` action' -> do
+        mSet actions
+        pure result
+
+     | otherwise ->
+        error
+          $ "Incorrect call to '" <> functionName <> "'\n"
+          <> "  called: '" <> showAction action <> "'\n"
+          <> "  expected a call to: '" <> showAction action' <> "'\n"
diff --git a/src/Data/Component/Mock/TH.hs b/src/Data/Component/Mock/TH.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Component/Mock/TH.hs
@@ -0,0 +1,31 @@
+module Data.Component.Mock.TH
+  (makeMock) where
+
+import Relude
+
+import qualified Language.Haskell.TH as Meta
+
+import qualified Data.Component.Mock.TH.Gadt as Gadt
+import qualified Data.Component.Mock.TH.Instance as Instance
+import qualified Data.Component.Mock.TH.NewFunction as NewFunction
+
+{-| Generates all the required things to work with a mock:
+* A data type representing the component methods (Actions)
+* The proper instantiation of 'IsAction' for this data type
+* A 'mock' function to be used instead of 'new'.
+
+The only requirement is that all the methods of the component
+must return something wrapped in a context.
+-}
+makeMock :: Meta.Name -> Meta.DecsQ
+makeMock componentName = do
+  info <- Meta.reify componentName
+  case info of
+    Meta.TyConI (Meta.DataD _ recordName _ _ [Meta.RecC _ fields] _) -> do
+      action <- Gadt.make fields
+      instance' <- Instance.make fields
+      newFunction <- NewFunction.make recordName fields
+      pure $ action <> [instance'] <> newFunction
+
+    _ ->
+      fail "makeMock only accepts simple records"
diff --git a/src/Data/Component/Mock/TH/Common.hs b/src/Data/Component/Mock/TH/Common.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Component/Mock/TH/Common.hs
@@ -0,0 +1,97 @@
+module Data.Component.Mock.TH.Common
+  ( actionName
+  , titleizeName
+  , functionTypeToList
+  , makePrimeVars
+  , makeVars
+  , Field(..)
+  , toField
+  , toVarExp
+  ) where
+
+import Relude
+import Data.Char
+
+import Language.Haskell.TH as Meta
+import Language.Haskell.TH.Syntax as Meta
+
+-- | The default name for the action
+actionName :: Meta.Name
+actionName = Meta.mkName "Action"
+
+-- | Converts a name to the equivalent titleized
+--
+-- >> titleizeName $ mkName "action"
+-- Name "Action"
+titleizeName :: Meta.Name -> Meta.Name
+titleizeName name =
+  Meta.nameBase name
+  & capitalizeFirst
+  & Meta.mkName
+ where
+  capitalizeFirst (c : cs) = toUpper c : cs
+  capitalizeFirst other = other
+
+-- | Converts a function type into a list of types, skipping the arrows
+functionTypeToList :: Meta.Type -> [Meta.Type]
+functionTypeToList type' =
+  case type' of
+    Meta.AppT (Meta.AppT Meta.ArrowT t1) t2 ->
+      t1: functionTypeToList t2
+
+    Meta.SigT t _ ->
+      functionTypeToList t
+
+    Meta.ForallT _ _ t ->
+      functionTypeToList t
+
+    t ->
+      [t]
+
+-- | Creates a list of n elements from
+-- the sequence from a' to z'
+makePrimeVars :: Int -> [Meta.Pat]
+makePrimeVars n =
+  ['a'..'z']
+  & fmap one
+  & fmap (<> "'")
+  & take n
+  & makeVarsWith
+
+-- | Creates a list of n elements from
+-- the sequence from a to z
+makeVars :: Int -> [Meta.Pat]
+makeVars n =
+  ['a'..'z']
+  & fmap one
+  & take n
+  & makeVarsWith
+
+data Field = Field
+  { name :: Meta.Name
+  , argumentsLength :: Int
+  }
+
+toField :: Meta.VarBangType -> Field
+toField (fieldName, _, fieldType) = do
+  let functionTypes = functionTypeToList fieldType
+  Field
+    { name = fieldName
+    , argumentsLength = length functionTypes - 1
+    }
+
+toVarExp :: Meta.Pat -> Meta.Exp
+toVarExp (Meta.VarP varName) =
+  Meta.VarE varName
+toVarExp other =
+  error
+    $ "Mockazo: Error when running 'toVarExp' for value '" <> show other <> "'\n"
+    <> "Please file an issue for this at https://github.com/theam/mockazo/issues"
+
+makeVarsWith :: [String] -> [Meta.Pat]
+makeVarsWith varNames =
+  varNames
+  & fmap makeVar
+ where
+  makeVar varName =
+    Meta.VarP (Meta.mkName varName)
diff --git a/src/Data/Component/Mock/TH/Gadt.hs b/src/Data/Component/Mock/TH/Gadt.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Component/Mock/TH/Gadt.hs
@@ -0,0 +1,104 @@
+module Data.Component.Mock.TH.Gadt
+  ( make
+  ) where
+
+import Relude
+import qualified Relude.Unsafe as Unsafe
+
+import qualified Language.Haskell.TH as Meta
+import qualified Language.Haskell.TH.Syntax as Meta
+
+import Data.Component.Mock.TH.Common
+
+{-| Generates a GADT definition for actions for
+a record of functions.
+
+For example, for a record like
+
+@
+data Component context = Component
+  { info    :: Text -> context ()
+  , success :: Text -> context ()
+  , fail    :: Text -> context ()
+  , debug   :: Text -> context ()
+  , start   :: Text -> context ()
+  , ask     :: Text -> Text -> context Text
+  }
+@
+
+it will generate the actions:
+
+@
+data Action a where
+  Info    :: Text -> Action ()
+  Success :: Text -> Action ()
+  Fail    :: Text -> Action ()
+  Debug   :: Text -> Action ()
+  Start   :: Text -> Action ()
+  Ask     :: Text -> Text -> Action Text
+@
+-}
+make :: [Meta.VarBangType] -> Meta.DecsQ
+make fields = do
+  constructors <- traverse constructorFromField fields
+  let gadtDefinition = defaultDefinition constructors
+  pure (gadtDefinition : derivedInstances)
+
+-- | Generates a GADT constructor based on a record field
+constructorFromField :: Meta.VarBangType -> Meta.Q Meta.Con
+constructorFromField (fieldName, _, type') =
+  case functionTypeToList type' of
+    result : [] -> do
+      resultType <- substituteReturnContext result
+      let name = titleizeName fieldName
+      pure $ Meta.GadtC [name] [] resultType
+    othersAndResult -> do
+      let resultType = Unsafe.last othersAndResult
+      let inputType = Unsafe.init othersAndResult
+      resultType <- substituteReturnContext resultType
+      let name = titleizeName fieldName
+      pure $ Meta.GadtC [name] (toList $ fmap (noBang,) inputType) resultType
+
+derivedInstances :: [Meta.Dec]
+derivedInstances =
+  fmap deriveInstanceFor ["Eq", "Show"]
+ where
+  deriveInstanceFor classStr =
+    Meta.StandaloneDerivD
+      Nothing
+      []
+      (Meta.AppT
+        (Meta.ConT $ Meta.mkName classStr)
+        (Meta.AppT
+          (Meta.ConT actionName)
+          (Meta.VarT $ Meta.mkName "r")))
+
+-- | Substitutes the context type variable by Action
+substituteReturnContext :: Meta.Type -> Meta.Q Meta.Type
+substituteReturnContext resultType =
+  case resultType of
+    Meta.AppT (Meta.VarT _) res ->
+      pure $ Meta.AppT (Meta.ConT actionName) res
+
+    other -> do
+      fail
+        $ "Data.Component.Mock only works with records that return values in a context, but got:\n"
+        <> "  - " <> Meta.pprint other
+
+-- | Omit strictness and unpacking
+noBang :: Meta.Bang
+noBang = Meta.Bang Meta.NoSourceUnpackedness Meta.NoSourceStrictness
+
+-- | Generate a GADT definition based on a list of constructors
+defaultDefinition :: [Meta.Con] -> Meta.Dec
+defaultDefinition constructors =
+  Meta.DataD context actionName typeVars kind constructors derivingClauses
+ where
+  context =
+    []
+  typeVars =
+    [Meta.PlainTV $ Meta.mkName "a"]
+  kind =
+    Nothing
+  derivingClauses =
+    []
diff --git a/src/Data/Component/Mock/TH/Instance.hs b/src/Data/Component/Mock/TH/Instance.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Component/Mock/TH/Instance.hs
@@ -0,0 +1,99 @@
+module Data.Component.Mock.TH.Instance
+  ( make
+  ) where
+
+import Relude
+
+import qualified Language.Haskell.TH as Meta
+import qualified Language.Haskell.TH.Syntax as Meta
+
+import Data.Component.Mock.TH.Common
+
+{-| Generates an instance for IsAction based on a record.
+
+For a record like
+
+@
+data Component context = Component
+  { rootDir          :: ProjectName -> context (Path Rel Dir)
+  , getProjectConfig :: ProjectName -> context ProjectConfig
+  , initProject      :: Path Rel Dir -> ProjectConfig -> context ()
+  }
+@
+
+It will generate an instance like
+@
+instance IsAction Action where
+  eqAction (RootDir a) (RootDir b) =
+    if a == b then Just Refl else Nothing
+
+  eqAction (GetProjectConfig a) (GetProjectConfig b) =
+    if a == b then Just Refl else Nothing
+
+  eqAction (InitProject a a') (InitProject b b') =
+    if a == b && a' == b' then Just Refl else Nothing
+
+  eqAction _ _ =
+    Nothing
+@
+-}
+make :: [Meta.VarBangType] -> Meta.DecQ
+make rawFields = do
+  let fields = fmap toField rawFields
+  pure $ Meta.InstanceD
+    Nothing
+    []
+    (Meta.AppT (Meta.ConT $ Meta.mkName "IsAction") (Meta.ConT actionName))
+    (functionDeclarations fields)
+
+functionDeclarations :: [Field] -> [Meta.Dec]
+functionDeclarations fields =
+  [ Meta.FunD
+      (Meta.mkName "eqAction")
+      (fmap makeClause fields <> [wildcardClause])
+    ]
+
+makeClause :: Field -> Meta.Clause
+makeClause Field{..} =
+  Meta.Clause
+    [ Meta.ConP (titleizeName name) (makeVars argumentsLength)
+    , Meta.ConP (titleizeName name) (makePrimeVars argumentsLength)
+    ]
+    (Meta.NormalB
+      (Meta.CondE
+        (makeCondition argumentsLength)
+        (Meta.AppE
+          (Meta.ConE $ Meta.mkName "Just")
+          (Meta.ConE $ Meta.mkName "Refl"))
+        (Meta.ConE $ Meta.mkName "Nothing")))
+    []
+
+wildcardClause :: Meta.Clause
+wildcardClause =
+  Meta.Clause
+    [ Meta.WildP
+    , Meta.WildP
+    ]
+    (Meta.NormalB
+      (Meta.ConE $ Meta.mkName "Nothing"))
+    []
+
+makeCondition :: Int -> Meta.Exp
+makeCondition varNumber = do
+  let vars = fmap toVarExp $ makeVars varNumber
+  let primeVars = fmap toVarExp $ makePrimeVars varNumber
+  let comparedVars = zipWith compareVars vars primeVars
+  case comparedVars of
+    [condition] ->
+      condition
+    (condition : moreConditions) ->
+      foldl' joinConditions condition moreConditions
+    other ->
+      error
+      $ "Mockazo: Error when running 'makeCondition' for value '" <> show other <> "'\n"
+      <> "Please file an issue for this at https://github.com/theam/mockazo/issues"
+ where
+  compareVars v v' =
+    Meta.UInfixE v (Meta.VarE (Meta.mkName "==")) v'
+  joinConditions c c' =
+    Meta.UInfixE c (Meta.VarE (Meta.mkName "&&")) c'
diff --git a/src/Data/Component/Mock/TH/NewFunction.hs b/src/Data/Component/Mock/TH/NewFunction.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Component/Mock/TH/NewFunction.hs
@@ -0,0 +1,57 @@
+module Data.Component.Mock.TH.NewFunction
+  (make) where
+
+import Relude
+
+import qualified Language.Haskell.TH as Meta
+import qualified Language.Haskell.TH.Syntax as Meta
+
+import Data.Component.Mock.TH.Common
+
+make :: Meta.Name -> [Meta.VarBangType] -> Meta.DecsQ
+make recordName fields = pure $
+  [ Meta.SigD
+      mockName
+      (Meta.ForallT
+        []
+        [ Meta.AppT
+            (Meta.AppT
+              (Meta.ConT $ Meta.mkName "Executes")
+              (Meta.ConT $ Meta.mkName "Action"))
+            (Meta.VarT $ Meta.mkName "actions")
+        ]
+        (Meta.AppT
+          (Meta.ConT recordName)
+          (Meta.AppT
+            (Meta.ConT $ Meta.mkName "InContextOf")
+            (Meta.VarT $ Meta.mkName "actions"))))
+  , Meta.ValD
+     (Meta.VarP mockName)
+     (Meta.NormalB
+       (Meta.RecConE
+         (eraseNameInformation recordName)
+         (fmap (mockField . toField) fields)
+         )) []
+  ]
+
+mockField :: Field -> (Meta.Name, Meta.Exp)
+mockField Field{..} = do
+  let varExps = fmap toVarExp $ makeVars argumentsLength
+  let actionConstructor = Meta.ConE (titleizeName name)
+  let appliedConstructor = foldl' Meta.AppE actionConstructor varExps
+  ( name
+    , Meta.LamE
+      (makeVars argumentsLength)
+      (Meta.AppE
+        (Meta.AppE
+          (Meta.VarE $ Meta.mkName "mockAction")
+          (Meta.LitE $ Meta.StringL $ Meta.nameBase name))
+        appliedConstructor)
+    )
+
+mockName :: Meta.Name
+mockName = Meta.mkName "mock"
+
+eraseNameInformation :: Meta.Name -> Meta.Name
+eraseNameInformation =
+  Meta.mkName . Meta.nameBase
diff --git a/test/Mock/HigherKinded.hs b/test/Mock/HigherKinded.hs
new file mode 100644
--- /dev/null
+++ b/test/Mock/HigherKinded.hs
@@ -0,0 +1,22 @@
+module Mock.HigherKinded
+  ( Action(..)
+  , Component(..)
+  , mock
+  , test
+  ) where
+
+import Relude
+import Data.Component.Mock
+
+data Component context = Component
+  { foo :: Maybe Int -> Maybe Int -> context ()
+  , bar :: Maybe Text -> context (Maybe Int)
+  }
+
+makeMock ''Component
+
+test :: Monad context => Component context -> context ()
+test Component{..} = do
+  x <- bar (Just "x")
+  y <- bar (Just "y")
+  foo x y
diff --git a/test/Mock/SingleKinded.hs b/test/Mock/SingleKinded.hs
new file mode 100644
--- /dev/null
+++ b/test/Mock/SingleKinded.hs
@@ -0,0 +1,22 @@
+module Mock.SingleKinded
+  ( Action(..)
+  , Component(..)
+  , mock
+  , test
+  ) where
+
+import Relude
+import Data.Component.Mock
+
+data Component context = Component
+  { foo :: Int -> Int -> context ()
+  , bar :: Text -> context Int
+  }
+
+makeMock ''Component
+
+test :: Monad context => Component context -> context ()
+test Component{..} = do
+  x <- bar "x"
+  y <- bar "y"
+  foo x y
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,31 @@
+import Relude
+
+import Test.Hspec
+import Data.Component.Mock
+
+import qualified Mock.SingleKinded as SingleKinded
+import qualified Mock.HigherKinded as HigherKinded
+
+main :: IO ()
+main = hspec $ do
+  describe "mockazo" $ do
+    describe "mocking components" $ do
+      it "handles single kinded types" $ do
+        let mock = SingleKinded.mock
+        runMock
+          $ withActions
+            [ SingleKinded.Bar "x" :-> 3
+            , SingleKinded.Bar "y" :-> 4
+            , SingleKinded.Foo 3 4 :-> ()
+            ]
+          $ SingleKinded.test mock
+
+      it "handles (fully applied) higher kinded types" $ do
+        let mock = HigherKinded.mock
+        runMock
+          $ withActions
+            [ HigherKinded.Bar (Just "x") :-> Just 2
+            , HigherKinded.Bar (Just "y") :-> Just 3
+            , HigherKinded.Foo (Just 2) (Just 3) :-> ()
+            ]
+          $ HigherKinded.test mock
