# \[ANN\] mockcat: A simple and flexible mock for test

**URL:** <https://discourse.haskell.org/t/ann-mockcat-a-simple-and-flexible-mock-for-test/10086>\
**Category:** Announcements\
**Created:** [August 3, 2024, 3:38pm UTC](https://discourse.haskell.org/t/ann-mockcat-a-simple-and-flexible-mock-for-test/10086 "2024-08-03T15:38:58Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![funnycat](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/funnycat/32/4523_2.png) [@funnycat](https://discourse.haskell.org/u/funnycat)\
**Post date:** [August 3, 2024, 3:38pm UTC](https://discourse.haskell.org/t/ann-mockcat-a-simple-and-flexible-mock-for-test/10086/1 "2024-08-03T15:38:58Z")

</div>

[mockcat](https://hackage.haskell.org/package/mockcat) is a simple yet flexible mock library.

mockcat provides capabilities for generating stub functions and verifying them.

Let me explain that.

In this post, we will provide usage examples for the following four.  
(Only basic functions are used in the description. Please refer to the documentation for various usages.):

1. A very simple example
2. Testing functions using type classes
3. Testing functions using type classes in combination with HMock
4. Testing functions using Extensible Effects (Polysemy)

**1. A Very Simple Example**  
Let’s create a stub function of type `String -> String`.

```haskell
import Test.MockCat (createStubFn, (|>))

main :: IO ()
main = do
  f <- createStubFn $ "input" |> "output"
  print $ f "input" -- "output"

```

Next, let’s create a stub function of type `String -> Reader String String`.

```haskell
import Test.MockCat (createStubFn, (|>))
import Control.Monad.Reader (Reader, ask, runReader)
import Data.Function ((&))

main :: IO ()
main = do
  f <- createStubFn $ "input" |> do
    e <- ask
    pure @(Reader String) $ "env is: " <> e

  f "input"
    & flip runReader "option value"
    & print -- "env is: option value"

```

**2. Testing Functions Using Type Classes**  
Let’s see an example of testing a function using type classes.  
Suppose we have a function program using the following type class.

```haskell
class (Monad m) => FileOperation m where
  readFile :: FilePath -> m Text
  writeFile :: FilePath -> Text -> m ()

program ::
  (FileOperation m) =>
  FilePath ->
  FilePath ->
  (Text -> Text) ->
  m ()
program inputPath outputPath modifyText = do
  content <- readFile inputPath
  let modifiedContent = modifyText content
  writeFile outputPath modifiedContent

```

When writing tests for this function, we want to confirm the following:

1. readFile is called with inputPath.
2. The result of readFile is passed to modifyText.
3. writeFile is called with outputPath and the result of modifyText.

Here is a test using the ReaderT monad (a bit verbose perhaps):

```haskell
data Functions = Functions
  { _readFile :: FilePath -> Text,
    _writeFile :: FilePath -> Text -> ()
  }

instance Monad m => FileOperation (ReaderT Functions m) where
  readFile path = ask >>= \f -> pure $ f._readFile path
  writeFile path content = ask >>= \f -> pure $ f._writeFile path content

spec :: Spec
spec = do
  it "Read, edit, and output files" do
    readFileStub <- createStubFn $ "input.txt" |> pack "content"
    writeFileMock <- createMock $ "output.text" |> pack "modifiedContent" |> ()
    modifyContentStub <- createStubFn $ pack "content" |> pack "modifiedContent"

    let functions = Functions {
      _readFile = readFileStub,
      _writeFile = stubFn writeFileMock }

    result <- runReaderT
      (program "input.txt" "output.text" modifyContentStub)
      functions

    result `shouldBe` ()
    writeFileMock `shouldApplyTo` ("output.text" |> pack "modifiedContent")

```

Next, I’ll show how it can be simplified using a library like HMock.

```haskell
makeMockable [t|FileOperation|]

spec :: Spec
spec = do
  it "Read, edit, and output files" do
    modifyContentStub <- createStubFn $ pack "content" |> pack "modifiedContent"

    result <- runMockT $ do
      expect $ ReadFile "input.txt" |-> pack "content"
      expect $ WriteFile "output.text" (pack "modifiedContent") |-> ()
      program "input.txt" "output.text" modifyContentStub

    result `shouldBe` ()

```

**4. Testing Functions Using Extensible Effects (Polysemy)**  
Finally, let’s write a test using Extensible Effects (Polysemy).  
Here is the program function rewritten using Polysemy.

```haskell
data FileOperation m a where
  ReadFile :: FilePath -> FileOperation m Text
  WriteFile :: FilePath -> Text -> FileOperation m ()

makeSem ''FileOperation

program ::
  Member FileOperation r =>
  FilePath ->
  FilePath ->
  (Text -> Text) ->
  Sem r ()
program inputPath outputPath modifyText = do
  content <- readFile inputPath
  let modifiedContent = modifyText content
  writeFile outputPath modifiedContent

```

And here is the corresponding test. We use handlers to utilize stub functions.

```haskell
spec :: Spec
spec = do
  it "Read, edit, and output files" do
    readFileStub <- createStubFn $ "input.txt" |> pack "content"
    writeFileMock <- createMock $ "output.text" |> pack "modifiedContent" |> ()
    modifyContentStub <- createStubFn $ pack "content" |> pack "modifiedContent"

    let runFileOperation :: Sem (FileOperation : r) a -> Sem r a
        runFileOperation = interpret $ \case
          ReadFile path -> pure $ readFileStub path
          WriteFile path text -> pure $ stubFn writeFileMock path text

    result <-
      program "input.txt" "output.text" modifyContentStub
        & runFileOperation
        & runM

    result `shouldBe` ()
    writeFileMock `shouldApplyTo` ("output.text" |> pack "modifiedContent")

```

That’s it for the explanation. If you find it interesting, please try it out and give us your feedback.

By the way, you can find the code used in this explanation here:

> **[GitHub - pujoheadsoft/mockcat-examples](https://github.com/pujoheadsoft/mockcat-examples)**
>
> Contribute to pujoheadsoft/mockcat-examples development by creating an account on GitHub.
