mockcat
Stub Haskell functions and verify calls when needed.
https://github.com/pujoheadsoft/mockcat#readme
| LTS Haskell 24.58: | 0.5.5.0 |
| Stackage Nightly 2026-09-13: | 1.5.0.0 |
| Latest on Hackage: | 1.5.0.0 |
mockcat-1.5.0.0@sha256:18c185b4aaa2366faa7513899d43fc32fa050ae907653541ceef262b1b753f58,5437Module documentation for 1.5.0.0
- Test
- Test.MockCat
- Test.MockCat.Cons
- Test.MockCat.Internal
- Test.MockCat.Internal.Builder
- Test.MockCat.Internal.GHC
- Test.MockCat.Internal.Message
- Test.MockCat.Internal.MockRegistry
- Test.MockCat.Internal.Registry
- Test.MockCat.Internal.Types
- Test.MockCat.Internal.Verify
- Test.MockCat.Mock
- Test.MockCat.MockT
- Test.MockCat.Param
- Test.MockCat.TH
- Test.MockCat.Verify
- Test.MockCat.WithMock
- Test.MockCat
Mockcat is a test double library for Haskell.
Its two core functions are stub and mock.
Defining a stub takes just one line:
-- Stub: Returns True when given "a"
let f :: String -> Bool
f = stub ("a" ~> True)
f "a" -- => True
Write the argument on the left of ~> and the return value on the right.
No verificationâjust a pure function.
When you need to verify calls, use mock.
With expects, you can declare expectations before execution:
withMockIO $ do
f <- mock ("a" ~> True)
`expects` called once -- Declare "should be called exactly once"
f "a" `shouldBe` True
-- Verification runs when exiting the withMockIO scope
Recommended usage:
- If an ordinary value or lambda is enough, use it directly.
- Need fixed return values? Use
stub(pure, no verification).- Need to verify arguments, call counts, or order? Use
mock.
Concepts & Terminology
Mockcat separates stubbing return values from verifying calls.
-
Stub: Returns configured values for expected arguments. Use it when you do not need to verify call history.
-
Mock: Adds call history to a stub, allowing you to verify that it was called as expected. Expectations can be verified after execution or declared before execution.
Why Mockcat?
Pure logic can be tested directly with values and functions, so it usually needs neither stubs nor mocks. When you do need a test double, Mockcat provides a small, straightforward way to define its behavior and verify calls when necessary.
Mockcat lets you declaratively describe function behavior and calls without depending on a specific architecture.
It works with ordinary functions, functions returning IO, functions passed as
arguments, Service Handle fields, and MTL/Capability typeclasses.
Mockcat follows your architectureânot the other way around.
Before / After
Here is what the same test setup looks like with Mockcat.
| Before: Handwritten⊠đ« | After: Mockcat đ±âš | |
|---|---|---|
| Definition (Stub)âI want to returnthis value for this argâ | f :: String -> IO Stringf arg = case arg of âaâ -> pure âbâ _ -> error âunexpectedâEven simple branching consumes many lines. | â Use stub if verification is unneeded (Pure)let f = stub (âaâ ~> âbâ)Behaves as a completely pure function. |
| Verify(Did it get calledcorrectly?) | â Need manual recording mechanismref <- newIORef []let f arg = do modifyIORef ref (arg:) âŠâ Verification Logiccalls <- readIORef refcalls shouldBe [âaâ]_ This is just one example. Boilerplate often grows._ |
withMock $ do â Declare expected values at definition f <- mock (âaâ ~> âbâ) `expects` called once â Just execute (Automatic verification)Recording is automatic.Focus on the âWhyâ and âWhatâ, not the âHowâ. |
Key Features
- Haskell Native DSL: No need to memorize redundant data constructors or specialized notation. Write test doubles naturally, just like function definitions (
arg ~> return). - Architecture Agnostic: Whether using MTL (Typeclasses), Service Handle (Records), or functionsâMockcat adapts to the production design you already have.
- Verify by âConditionâ, not just Value: Works even if arguments lack
Eqinstances. You can verify based on âwhat properties it should satisfyâ (Predicates) rather than just strict equality. - Helpful Error Messages: Shows âstructural diffsâ on failure, highlighting exactly what didnât match.
function was not called with the expected arguments. Closest match: expected: Record { name = "Alice", age = 20 } but got: Record { name = "Alice", age = 21 } ^^^ Specific difference in `age`: expected: 20 but got: 21 ^^ - Intent-Driven Types: Types help express testing intent without imposing a particular architecture.
Quick Start
Copy and paste the code below to experience Mockcat right now.
Installation
package.yaml:
dependencies:
- mockcat
Or .cabal:
build-depends:
mockcat
First Test (No Verification)
import Test.Hspec
import Test.MockCat
spec :: Spec
spec = do
it "stub demo" $ do
let f :: String -> Int
f = stub ("Hello" ~> 42)
f "Hello" `shouldBe` 42
First Test (With Verification)
import Test.Hspec
import Test.MockCat
spec :: Spec
spec = do
it "mock demo" $ do
withMockIO $ do
-- Declare "should be called exactly once"
f <- mock ("Hello" ~> (42 :: Int))
`expects` called once
f "Hello" `shouldBe` 42
-- Verification runs when exiting the withMockIO scope
User Guide
Mockcat supports two verification styles, depending on when you want to state the expectations.
1. Declarative Verification (withMock (withMockIO) / expects)
Declare expectations when defining the mock. Verification runs automatically when the scope exits. This keeps the mock definition and its expectations close together.
import Test.Hspec
import Test.MockCat
import Control.Monad.IO.Class (MonadIO(liftIO))
spec :: Spec
spec = do
it "User Guide (withMock)" $ do
withMock $ do
-- Define a mock that returns True for "Hello"
f <- mock ("Hello" ~> True)
`expects` called once
-- Execution
let result = f "Hello"
liftIO $ result `shouldBe` True
withMockIO: Simplified IO Testing
withMockIO is an IO-specialized version of withMock. It allows you to run IO actions directly within the mock context without needing liftIO.
import Test.Hspec
import Test.MockCat
spec :: Spec
spec = do
it "User Guide (withMockIO)" $ do
withMockIO $ do
f <- mock ("Hello" ~> True)
`expects` called once
let result = f "Hello"
result `shouldBe` True
[!IMPORTANT] When using
expects(declarative verification), you MUST wrap the mock definition in parentheses(...). The$operator pattern used in previous versions (mock $ ... expects ...) will cause compilation errors due to precedence changes.â
mock $ any ~> True expects ...âmock (any ~> True) expects ...
[!NOTE] You can also use
expectsfor declarative verification insiderunMockTblocks. This works seamlessly with generated typeclass mocks as well.runMockT do _readFile ("config.txt" ~> pure "value") `expects` called once
2. Mocking with Typeclass-Based Designs (makeMock)
For designs that express dependencies via typeclasses (MTL style or Capability pattern), Mockcat can generate mocks from those typeclasses using Template Haskell. This option is for designs that already use typeclasses; you do not need to introduce a typeclass just for testing.
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
class Monad m => FileSystem m where
readFile :: FilePath -> m String
writeFile :: FilePath -> String -> m ()
-- [Strict Mode] Default behavior. Consistent with 'mock'.
-- If the return type is `m a`, the stub definition must return a value of type `m a` (e.g., `pure @IO "value"`, `throwIO Error`).
-- Recommended when you prefer explicit descriptions faithful to Haskell's type system.
makeMock [t|FileSystem|]
-- [Auto-Lift Mode] Convenience-focused mode.
-- Automatically wraps pure values into the monad (m String).
makeAutoLiftMock [t|FileSystem|]
[!NOTE] If the class definition requires additional extensions (e.g.,
MultiParamTypeClasses,UndecidableInstances), Mockcat will display a detailed error message during compilation to guide you.
Use runMockT block in your tests.
spec :: Spec
spec = do
it "filesystem test" do
result <- runMockT do
-- [Strict Mode] (if using makeMock)
_readFile $ "config.txt" ~> pure @IO "debug=true"
_writeFile $ "log.txt" ~> "start" ~> pure @IO ()
-- [Auto-Lift Mode] (if using makeAutoLiftMock)
-- _readFile $ "config.txt" ~> "debug=true"
-- Run code under test (mock injected)
myProgram "config.txt"
result `shouldBe` ()
3. Function Mocking and Post-Verification (mock / shouldBeCalled)
Create a mock, exercise the code under test, and then verify the recorded calls
with shouldBeCalled. Use this style when verification reads more naturally
after execution.
import Test.Hspec
import Test.MockCat
spec :: Spec
spec = do
it "Function Mocking" $ do
-- Define a mock that returns True for "Hello" (No 'expects' here)
f <- mock ("Hello" ~> True)
-- Execution
f "Hello" `shouldBe` True
-- Post-Verification (shouldBeCalled)
f `shouldBeCalled` "Hello"
[!WARNING] Limitation in HPC (Code Coverage) Environments Do not use
shouldBeCalledwhen running tests withstack test --coverageor similar. The code coverage instrumentation by GHC wraps functions, which changes their identity and causes verification to fail. If you need code coverage, please use theexpectsstyle (Section 1).
Flexible Matching: You can specify conditions (predicates) instead of concrete values.
{-# LANGUAGE TypeApplications #-}
import Test.Hspec
import Test.MockCat
import Prelude hiding (any)
spec :: Spec
spec = do
it "Matcher Examples" $ do
-- Arbitrary string (param any)
f <- mock (any @String ~> True)
f "foo" `shouldBe` True
-- Condition (when)
g <- mock (when (> (5 :: Int)) "> 5" ~> True)
g 6 `shouldBe` True
4. Flexible Verification (Matchers)
Even if arguments donât have Eq instances, or you donât want to depend on specific values, you can verify based on intentââwhat condition should be metâ.
Mockcat provides matchers for argument properties, not just exact value equality.
Allow Any Value (any)
-- Return True regardless of the argument
f <- mock (any ~> True)
-- Verify that it was called (arguments don't matter)
f `shouldBeCalled` any
Verify with Conditions (when)
You can use conditions (predicates) instead of exact values.
This is useful for types without Eq (such as functions), or when checking partial matches.
-- Return False only if the argument starts with "error"
f <- mock do
onCase $ when (\s -> "error" `isPrefixOf` s) "start with error" ~> False
onCase $ any ~> True
If you donât need a label (description shown on error), you can use when_.
f <- mock (when_ (> 5) ~> True)
5. Advanced Features - [Advanced]
mock vs stub vs mockM
These functions differ in whether they record calls and where that recording takes place.
| Function | Verification (shouldBeCalled) |
IO Dependency | Characteristics |
|---|---|---|---|
stub |
â | None | Pure Stub. No IO dependency. Sufficient if verification isnât needed. |
mock |
â | None (External) | Mock. Behaves as a pure function. Automatically records history. |
mockM |
â | Yes (Explicit) | Monadic Mock. Used within MockT or IO, allowing explicit handling of side effects (e.g., logging). |
Choosing between mock and mockM
Choose between mock and mockM according to the return type of the target function.
mock(For Pure Functions):- Use this when mocking pure functions like
String -> Int. - It respects Haskellâs lazy evaluation and records the call only when the result is actually evaluated. This prevents counting unnecessary calls that were never executed.
- Use this when mocking pure functions like
[!IMPORTANT] Since
mockbehaves as a pure function (a -> b), it is subject to GHCâs optimizations (CSE / CAF / full laziness).As a result, even if an expression appears multiple times in your source code, it may be evaluated only once after compilation.
Mockcat records and verifies the actual number of evaluations, not the number of times the expression appears in the source code.
mockM(For IO/Monadic Functions):- Use this when mocking functions that return
IOor otherMonadIOinstances (such asReaderT IO), likeString -> IO Int. - Since recording is built directly into the returned action (
IO), call counts remain predictable under concurrency and GHC optimizations.
- Use this when mocking functions that return
[!TIP] If the function returns IO, use
mockM. Otherwise, usemock.
Partial Mocking: Mixing with Real Functions
Useful when you want to replace only some methods with mocks while using real implementations for others.
-- [Strict Mode]
makePartialMock [t|FileSystem|]
-- [Auto-Lift Mode]
-- Just like makeAutoLiftMock, there is an Auto-Lift version for Partial Mock.
makeAutoLiftPartialMock [t|FileSystem|]
instance FileSystem IO where ... -- Real instance is also required
test = runMockT do
_readFile $ "test" ~> pure @IO "content" -- Only mock readFile (Strict)
-- or
-- _readFile $ "test" ~> "content" -- (Auto-Lift)
program -- writeFile runs the real IO instance
Derivation and Custom Instances
When using MockT, you might need to handle type classes that are not directly related to the side effects you are mocking. Mockcat provides macros to help with these cases.
MTL Instances (MonadReader, MonadError, etc.)
MockT provides standard mtl instances (MonadReader, MonadError, MonadState, MonadWriter) out of the box. These instances automatically lift operations to the base monad.
Custom Type Class Derivation (deriveMockInstances)
For custom âCapabilityâ type classes (like MonadLogger, MonadConfig) that should just be lifted to the base monad, use deriveMockInstances.
class Monad m => MonadLogger m where
logInfo :: String -> m ()
deriveMockInstances [t|MonadLogger|]
This generates an instance for MockT m that calls lift . logInfo.
Explicit No-op Instances (deriveNoopInstance)
Sometimes you want a mock to do nothing for certain methods (especially those returning m ()) without having to define explicit stubs or provide a base implementation.
class Monad m => MonadAuditor m where
audit :: String -> m ()
deriveNoopInstance [t|MonadAuditor|]
This generates an instance for MockT m where audit simply returns pure ().
Sequential Responses
Cases are matched from top to bottom, like Haskell pattern matching. Only the first matching case is selected. Each case owns its response sequence, and only calls selecting that case advance it. The final response is repeated after the sequence is exhausted.
In short, onCase describes branching by input; andThen describes the sequence
of responses for repeated calls to that case.
f <- mock do
onCase $ "A" ~> 1
`andThen` 2
`andThen` 3
onCase $ any @String ~> 9
`andThen` 10
`andThen` 11
-- Calls: A, B, A, C, A, B, A, C
-- Results: 1, 9, 2, 10, 3, 11, 3, 11
Later overlapping cases are unreachable. To return consecutive values for the
same condition, attach them to one case with andThen.
Monadic Return (IO a)
andThen also works with monadic return values when you want different effects
or results for consecutive calls.
f <- mock do
onCase $ "get" ~> pure @IO 1 -- 1st call
`andThen` pure @IO 2 -- 2nd and later calls
Named Mocks
You can attach labels to display function names in error messages.
f <- mock (label "myAPI") ("arg" ~> True)
Encyclopedia (Feature Reference)
â» Use this section as a dictionary when you get stuck.
Declarative Verification DSL (expects)
In expects blocks, you can describe expectations declaratively using a builder-style syntax.
It shares the same vocabulary as shouldBeCalled.
Basic Usage
Start with called and chain conditions.
-- Call count only
mock (any ~> True) `expects` called once
-- With arguments
mock (any ~> True) `expects` (called once `with` "arg")
-- Multiple expectations (in do block)
mock (any ~> True) `expects` do
called once `with` "A"
called once `with` "B"
Syntax Reference
| Builder | Description | Example |
|---|---|---|
called |
[Required] Starts the expectation builder. | called ... |
times n |
Expects exact call count. | called . times 2 |
once |
Alias for times 1. |
called . once |
never |
Expects 0 calls. | called . never |
with arg |
Expects specific argument(s). | called with "value" |
with matcher |
Uses a matcher for argument verification. | called with when (>5) "gt 5" |
inOrder |
Verify usage order (when used in a list) | (See âOrder Verificationâ section) |
Verification Matchers (shouldBeCalled)
| Matcher | Description | Example |
|---|---|---|
x (Value itself) |
Was called with that value | f `shouldBeCalled` (10 :: Int) |
times n |
Exact count | f `shouldBeCalled` (times 3 `with` "arg") |
once |
Exactly once | f `shouldBeCalled` (once `with` "arg") |
never |
Never called | f `shouldBeCalled` never |
atLeast n |
n or more times | f `shouldBeCalled` atLeast 2 |
atMost n |
n or fewer times | f `shouldBeCalled` atMost 5 |
anything |
Any argument (count ignored) | f `shouldBeCalled` anything |
inOrderWith [...] |
Strict order | f `shouldBeCalled` inOrderWith ["a", "b"] |
inPartialOrderWith [...] |
Partial order (skips allowed) | f `shouldBeCalled` inPartialOrderWith ["a", "c"] |
Parameter Matchers (Definition)
| Matcher | Description | Example |
|---|---|---|
any |
Any value | any ~> True |
when pred label |
Condition | when (>0) "positive" ~> True |
when_ pred |
No label | when_ (>0) ~> True |
FAQ
A. Since mock is treated as a pure function, GHCâs optimizations may cause evaluations to be shared.
This is Mockcatâs intended behaviorâit accurately reflects the actual execution result after compilation.
Mockcat records âthe actual number of evaluationsâ at runtime. Therefore, if the same expression is shared due to optimization, it is correctly counted as â1 callâ.
If you want to suppress evaluation sharing for testing purposes, you can add the following GHC pragmas to your test file.
{-# OPTIONS_GHC -fno-cse #-}
{-# OPTIONS_GHC -fno-full-laziness #-}
This is a test-only setting, useful when you want to verify behavior closer to the source-level call count.
Real-World Examples
Mockcat does not dictate production architecture. Use ordinary values and functions for pure code, and introduce Mockcat only at boundaries where a small, direct test double is useful.
Here are real-world test suites using mockcat:
-
MTL + Capability pattern: Port-based application tests (using
MockT/ExceptT) đ UsecaseSpec.hs (cli-mtl) -
Polysemy effects: Use stub for data flow, mock only where verification is needed đ UsecaseSpec.hs (cli-effect-polysemy)
Tips and Troubleshooting
Name collision with Prelude.any
The any parameter matcher from Test.MockCat may conflict with Prelude.any.
To resolve this, hide any from Prelude or use a qualified name.
import Prelude hiding (any)
-- or
import qualified Test.MockCat as MC
Name collision with Control.Monad.when
Test.MockCat exports when (parameter matcher), which may conflict with Control.Monad.when.
To avoid this, hide when from Test.MockCat or use qualified import.
import Test.MockCat hiding (when)
-- or
import Control.Monad hiding (when) -- if you want to use the matcher
Ambiguous types with OverloadedStrings
If you have OverloadedStrings enabled, string literals may cause ambiguity errors.
Add explicit type annotations to resolve this.
mock (("value" :: String) ~> True)
Tested Versions
mockcat is continuously tested in CI across these configurations:
| GHC | Cabal | OS |
|---|---|---|
| 9.2.8 | 3.10.3.0 / 3.12.1.0 | Ubuntu, macOS, Windows |
| 9.4.8 | 3.10.3.0 / 3.12.1.0 | Ubuntu, macOS, Windows |
| 9.6.7 | 3.12.1.0 | Ubuntu, macOS, Windows |
| 9.8.4 | 3.12.1.0 | Ubuntu, macOS, Windows |
| 9.10.3 | 3.12.1.0 | Ubuntu, macOS, Windows |
| 9.12.2 | 3.12.1.0 | Ubuntu, macOS, Windows |
Happy Mocking! đ±
Changes
Changelog for mockcat
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to the Haskell Package Versioning Policy.
[Unreleased]
[1.5.0.0] - 2026-09-12
Added
- Added
andThenfor declaring consecutive responses on a single mock case.
Changed
- Breaking Change:
onCasedefinitions now use first-match-wins semantics, matching Haskell pattern matching. Each selected case maintains its own consecutive-response position, and repeats its final response after exhaustion. Repeated overlappingonCasedefinitions no longer form an implicit response sequence; useandTheninstead.
[1.4.1.1] - 2026-01-17
Fixed
- Test Suite Stability: Added GHC optimization pragmas (
-fno-cse,-fno-full-laziness) to ExampleSpec.hs and WithMockSpec.hs to prevent test failures on certain GHC builds (notably unofficial Ubuntu 24.04 bindists for GHC 9.2.8).
[1.4.1.0] - 2026-01-12
Improved
- Mock Context Isolation: Redesigned mock tracking to use local context containers within
withMockandrunMockT. This significantly reduces reliance on global registries and thread-local storage, ensuring better test isolation and predictability. - Optimization Resilience Guidance: Added detailed documentation on choosing between
mockandmockM. For functions returningIO, usingmockMis the recommended way to ensure stability under GHC optimizations (like CSE) without requiring extra compiler flags. - Robust Registration logic: Re-implemented internal mock registration using a capability-based approach (
RegisterMockandHasMockContext), making the libraryâs core architecture more robust and easier to maintain.
[1.4.0.0] - 2026-01-10
Changed
- Breaking Change: Removed the verify fallback mechanism that searched thread history when
StableNamelookup failed.- Impact: Strict verification is now enforced. Tests using
shouldBeCalledor related matchers will now deterministically fail if the mock functionâs identity cannot be strictly resolved (e.g., in some HPC/Coverage environments without proper handling), rather than attempting to guess the intent. - Motivation: To ensure absolute confidence in test results and support reliable HPC coverage analysis.
- Impact: Strict verification is now enforced. Tests using
Improved
- HPC Code Coverage Support: The library and test suite are now fully compatible with
stack test --coverage.- Verification logic has been hardened to handle HPC instrumentation.
- The test suite now automatically detects HPC mode and skips only those tests that strictly depend on
StableNameidentity (which is inherently unstable under HPC), ensuring the rest of the suite runs safely.
- Test Suite Modernization: Refactored Property-based tests and
WithMockIOspecs to use theexpectspattern (definition-time expectation). This makes them robust enough to strictly verify behavior even in HPC environments. - Granularity: Improved module organization in
Spec.hsfor better maintainability.
[1.3.3.0] - 2026-01-04
Added
- Type Families Support:
deriveMockInstancesnow supports type classes containing Associated Type Families. - Improved
withMockIO:withMockIOnow enables direct execution of IO actions within mock contexts withoutliftIO.
[1.3.2.0] - 2026-01-04
Added
- New Derivation Macros:
deriveMockInstances: Automatically derivesMockTinstances for user-defined âCapabilityâ type classes (e.g.,MonadLogger). It lifts operations to the base monad.- Note: Type Families are currently not supported.
deriveNoopInstance: Generates âNo-opâ instances where methods (returningm ()) do nothing. Useful for ignoring specific interactions (e.g.,MonadAuditor).
Improved
- Compile-Time Verification: Enhanced robustness of internal verification scripts (
verify_th_errors.sh) to ensure consistent error reporting across GHC versions. - Documentation: Clarified âCapability vs. Controlâ design philosophy and limitations regarding Type Families in
deriveMockInstances.
[1.3.1.0] - 2026-01-03
Added
- New Parameter Matcher: Introduced
whenandwhen_as the primary functions for condition-based matching.- These replace
expectandexpect_to improve readability (e.g.,mock (when (>5) ~> True)reads naturally as âmock when > 5â).
- These replace
Changed
- Renaming / Deprecation:
expectandexpect_have been renamed towhenandwhen_.- The old
expectandexpect_are now DEPRECATED and will trigger a compiler warning. They will be removed in a future major release.
- The old
- Documentation: Updated
when/when_usage and added guidance on resolving name collisions withControl.Monad.when.
[1.3.0.0] - 2026-01-03
Added
- Type-Safe Verification Result: Generated mock helpers now return
MockResult params. This type carries parameter information, enabling robust type inference and compile-time safety checks when using declarative verification (expects).- This change is part of an ongoing effort to make misuse of mocks impossible at the type level.
Changed
- Breaking Change: Due to the introduction of
MockResult, generated mock helpers (e.g.,_myMethod) no longer return the mock function itself (MockT m (FunctionType)).- Code that previously relied on capturing the returned function (e.g.,
fn <- _myMethod ...) will need to be updated.
- Code that previously relied on capturing the returned function (e.g.,
- Refactoring: Reorganized internal test verification logic to utilize
MockResultproperties. - Internal: Refactored test suite organization for better maintainability.
[1.2.1.0] - 2026-01-01
Added
- Dynamic Language Extension Detection:
mockcatnow automatically identifies and requests only necessary extensions (e.g.,MultiParamTypeClasses,UndecidableInstances) based on the target class definition. - Granular Extension Optimization: Simple multi-parameter type classes no longer require
AllowAmbiguousTypesorFunctionalDependenciesunless they are actually used.
Fixed
- Resolved
NoVerificationscope issues in Template Haskell generated code. - Resolved ambiguous
anyoccurrences in internal Template Haskell logic. - Fixed several typos in Template Haskell error messages.
[1.2.0.0] - 2025-12-31
Changed
- Breaking Change: Changed the fixity of
expectsoperator toinfixl 0to resolve precedence issues with$.- Impact: Code using
mock $ ... expects ...will now fail to compile. - Migration: Wrap the mock definition in parentheses:
mock (... ~> ...) expects ....
- Impact: Code using
- Breaking Change:
expectsnow strictly enforces that it can only be applied to a mock creation action (m fn).- Attempting to apply
expectsdirectly to aMockSpec(e.g.(any ~> 1) expects ...) or an already instantiated function will result in a compile-timeTypeError.
- Attempting to apply
- Removed redundant
Typeableconstraints fromexpects, enabling cleaner builds on GHC 9.8+.
[1.1.0.0] - 2025-12-29
Added
- HPC Coverage Support: Verification logic now robustly handles unstable
StableNamescaused by HPC instrumentation (stack test --coverage). - Optimization Hardening: Protected verification logic against GHC optimizations (CSE/LICM) to ensure stable tests in optimized builds.
Changed
- Automatic History Reset:
runMockTandwithMocknow strictly scope mock history. History is automatically reset to prevent interference between sequential tests or Property-Based Testing iterations.
[1.0.0.0] - 2025-12-24
Changed
- DSL Reboot: Replaced
|>with~>as the primary parameter chain operator (representing the âmock arrowâ). - Terminology Shift: Standardized terminology to âcalledâ instead of âappliedâ throughout the library and error messages.
- Simplified creating/stubbing API:
f <- mock $ ...is now the canonical way. - Expanded structural diffing support for nested records and lists.
- Unified verification API: All verification is now handled via
shouldBeCalled. - Strict by Default:
makeMockandmakePartialMocknow default to strict return values (implicit monadic return is disabled).makeAutoLiftMockwas introduced for the previous behavior.
Added
- Deep Structural Diff: Enhanced error messages with precise caret pointers for complex nested data structures.
- STM-based concurrency for mock registration and call recording.
- Infinite arity support for mock/stub building.
Removed
- Backward compatibility with 0.x.x APIs (
stubFn,createMock,applied, etc.). makeMockWithOptions,makePartialMockWithOptions, andMockOptions(internalized to simplify API).
Migration Guide (0.x -> 1.0)
This release is a complete reboot. Previous code will break.
-
Operator Change: Replace
|>with~>.-- Old createStubFn $ "arg" |> "result" -- New stub $ "arg" ~> "result" -
Mock Creation: Use
mock/stubinstead ofcreateMock/createStubFn.-- Old f <- createMock $ "arg" |> "result" -- New f <- mock $ "arg" ~> "result" -
Verification: Use
shouldBeCalled(unified API).-- Old f `shouldApplyTo` "arg" -- New f `shouldBeCalled` "arg" -
Template Haskell Generics:
makeMockis now strict by default (requires explicitpurefor IO actions).- Use
makeAutoLiftMockfor old implicit behavior. - Or stick to
makeMockand addpureto your return values.
- Use
0.6.0.0
Changed
- Removed the upper limit on variable arguments when creating stub functions. Previously, there was a restriction on the maximum number of arguments, but this limitation has been removed, allowing stub functions to accept an arbitrary number of arguments.
0.5.5.0
Added
- Aliases
expectApplyTimesandexpectNever(preferred names) for pre-run expectation declarations.
Documentation
- README (EN/JA) now recommends
expectApplyTimes/expectNeverover legacyapplyTimesIs/neverApply. - Clarified that
expectApplyTimes nis the canonical form;expectNeveris sugar forexpectApplyTimes 0.
Notes
- Legacy names remain exported for backward compatibility (no deprecation pragma yet). They may receive a soft deprecation notice in a future minor release after community feedback.
0.5.4.0
Added
- Parallel execution support (verified counting under concurrency, stress tests).
- Verification helpers:
applyTimesIs,neverApply.
Changed
- Refactored
MockTfromStateTtoReaderT (TVar [Definition])architecture. - Simplified Template Haskell generated constraints.
Fixed
- Race causing lost/double count in concurrent stub applications (strict
modifyTVar').
Removed
unsafePerformIOin TH-generated code.
Internal
- Introduced
MonadMockDefsabstraction.
0.5.3.0
Added
MonadUnliftIOinstance forMockT(initial groundwork for later parallel support).