mockcat 1.3.0.0 → 1.3.1.0
raw patch · 13 files changed
+176/−44 lines, 13 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Test.MockCat.Param: when :: (a -> Bool) -> String -> Param a
+ Test.MockCat.Param: when_ :: (a -> Bool) -> Param a
Files
- CHANGELOG.md +9/−0
- README-ja.md +60/−18
- README.md +60/−18
- mockcat.cabal +1/−1
- src/Test/MockCat/Param.hs +22/−0
- src/Test/MockCat/TH.hs +1/−1
- test/Property/AdditionalProps.hs +1/−0
- test/Property/ReinforcementProps.hs +1/−0
- test/ReadmeVerifySpec.hs +2/−1
- test/Test/MockCat/ExampleSpec.hs +1/−0
- test/Test/MockCat/MockSpec.hs +16/−5
- test/Test/MockCat/ParamSpec.hs +1/−0
- test/Test/MockCat/ShouldBeCalledSpec.hs +1/−0
CHANGELOG.md view
@@ -6,6 +6,15 @@ and this project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/). +## [1.3.1.0] - 2026-01-03+### Added+- **New Parameter Matcher**: Introduced `when` and `when_` as the primary functions for condition-based matching.+ - These replace `expect` and `expect_` to improve readability (e.g., `mock (when (>5) ~> True)` reads naturally as "mock when > 5").++### Changed+- **Renaming / Deprecation**: `expect` and `expect_` have been renamed to `when` and `when_`.+ - The old `expect` and `expect_` are now **DEPRECATED** and will trigger a compiler warning. They will be removed in a future major release.+- **Documentation**: Updated `when` / `when_` usage and added guidance on resolving name collisions with `Control.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`).
README-ja.md view
@@ -135,7 +135,7 @@ | Matcher | Description | Example | | :--- | :--- | :--- | | **`any`** | どんな値でも許可 | `f <- mock (any ~> True)` |-| **`expect`** | 条件(述語)で検証 | `f <- mock (expect (> 5) "gt 5" ~> True)` |+| **`when`** | 条件(述語)で検証 | `f <- mock (when (> 5) "gt 5" ~> True)` | | **`"val"`** | 値の一致 (Eq) | `f <- mock ("val" ~> True)` | | **`inOrder`** | 順序検証 | ``f `shouldBeCalled` inOrderWith ["a", "b"]`` | | **`inPartial`**| 部分順序 | ``f `shouldBeCalled` inPartialOrderWith ["a", "c"]`` |@@ -167,8 +167,8 @@ -- 任意の文字列 (param any) f <- mock (any ~> True) --- 条件式 (expect)-f <- mock (expect (> 5) "> 5" ~> True)+-- 条件式 (when)+f <- mock (when (> 5) "> 5" ~> True) ``` ### 2. 型クラスのモック (`makeMock`)@@ -246,7 +246,13 @@ > [!NOTE] > `runMockT` ブロックの中でも、同様に `expects` を使った宣言的検証が可能です。-> つまり、「モック生成」と「期待値宣言」が1つのブロック内で完結する統一された体験を提供します。+> 生成された型クラスのモック関数(`_xxx`)に対してもそのまま使用できます。+>+> ```haskell+> runMockT do+> _readFile "config.txt" ~> pure "value"+> `expects` called once+> ``` ### 4. 柔軟な検証(マッチャー) @@ -263,7 +269,7 @@ f `shouldBeCalled` any ``` -#### 条件を指定して検証 (`expect`)+#### 条件を指定して検証 (`when`) 任意の値ではなく、「条件(述語)」を使って検証できます。 `Eq` を持たない型(関数など)や、部分的な一致を確認したい場合に強力です。@@ -271,10 +277,16 @@ ```haskell -- 引数が "error" で始まる場合のみ False を返す f <- mock do- onCase $ expect (\s -> "error" `isPrefixOf` s) "start with error" ~> False+ onCase $ when (\s -> "error" `isPrefixOf` s) "start with error" ~> False onCase $ any ~> True ``` +ラベル(エラー時に表示される説明)が不要な場合は、`when_` を使用することもできます。++```haskell+f <- mock (when_ (> 5) ~> True)+```+ ### 5. 高度な機能 - [応用] #### mock vs stub vs mockM の使い分け@@ -353,23 +365,43 @@ | マッチャ | 説明 | 例 | | :--- | :--- | :--- | | `any` | 任意の値 | `any ~> True` |-| `expect pred label` | 条件式 | `expect (>0) "positive" ~> True` |-| `expect_ pred` | ラベルなし | `expect_ (>0) ~> True` |+| `when pred label` | 条件式 | `when (>0) "positive" ~> True` |+| `when_ pred` | ラベルなし | `when_ (>0) ~> True` | ### 宣言的検証 DSL (`expects`) -`expects` ブロックでは、呼び出しに関する期待を宣言的に記述できます。-`expects` で使用できる構文は、`shouldBeCalled` と同じ語彙を共有しています。+`expects` ブロックでは、ビルダースタイルの構文を使って宣言的に期待値を記述できます。+`shouldBeCalled` と共通の語彙を使用しています。 -| 構文 | 意味 |-| :--- | :--- |-| `called` | 呼び出しに関する期待の開始 |-| `once` | 1 回だけ呼ばれる |-| `times n` | n 回呼ばれる |-| `never` | 呼ばれない |-| `with arg` | 引数の期待値 |-| `with matcher` | マッチャを用いた引数検証 |+#### 基本的な使い方 +`called` で開始し、条件を連鎖させて記述します。++```haskell+-- 回数のみ+mock (any ~> True) `expects` called once++-- 引数を指定+mock (any ~> True) `expects` (called once `with` "arg")++-- 複数の期待値 (do ブロック)+mock (any ~> True) `expects` do+ called once `with` "A"+ called once `with` "B"+```++#### 構文リファレンス++| Builder | 説明 | 例 |+| :--- | :--- | :--- |+| **`called`** | **[必須]** 期待値ビルダーを開始します。 | `called ...` |+| **`times n`** | 回数を指定します。 | `called . times 2` |+| **`once`** | `times 1` のエイリアス。 | `called . once` |+| **`never`** | 0回を期待します。 | `called . never` |+| **`with arg`** | 引数を指定します。 | `called `with` "value"` |+| **`with matcher`** | マッチャを使って引数を検証します。 | `called `with` when (>5) "gt 5"` |+| **`inOrder`** | 呼び出し順序を検証 (リスト内で使用) | (順序検証の項を参照) |+ ### よくある質問 (FAQ) <details>@@ -408,6 +440,16 @@ import Prelude hiding (any) -- または import qualified Test.MockCat as MC+```++### `when` と `Control.Monad.when` の名前衝突+`Test.MockCat` は `when` (パラメータマッチャ) をエクスポートするため、`Control.Monad` の `when` (条件分岐) と衝突することがあります。+その場合は `Test.MockCat` からの `when` を隠すか、修飾名を使用してください。++```haskell+import Test.MockCat hiding (when)+-- または+import Control.Monad hiding (when) -- モックの方を使いたい場合 ``` ### `OverloadedStrings` 使用時の型推論エラー
README.md view
@@ -135,7 +135,7 @@ | Matcher | Description | Example | | :--- | :--- | :--- | | **`any`** | Matches any value | `f <- mock (any ~> True)` |-| **`expect`** | Matches condition | `f <- mock (expect (> 5) "gt 5" ~> True)` |+| **`when`** | Matches condition | `f <- mock (when (> 5) "gt 5" ~> True)` | | **`"val"`** | Matches value (Eq) | `f <- mock ("val" ~> True)` | | **`inOrder`** | Order verification | ``f `shouldBeCalled` inOrderWith ["a", "b"]`` | | **`inPartial`**| Partial order | ``f `shouldBeCalled` inPartialOrderWith ["a", "c"]`` |@@ -169,8 +169,8 @@ -- Arbitrary string (param any) f <- mock (any ~> True) --- Condition (expect)-f <- mock (expect (> 5) "> 5" ~> True)+-- Condition (when)+f <- mock (when (> 5) "> 5" ~> True) ``` ### 2. Typeclass Mocking (`makeMock`)@@ -248,7 +248,13 @@ > [!NOTE] > You can also use `expects` for declarative verification inside `runMockT` blocks.-> This provides a unified experience where "Mock Creation" and "Expectation Declaration" complete within a single block.+> This works seamlessly with generated typeclass mocks as well.+>+> ```haskell+> runMockT do+> _readFile "config.txt" ~> pure "value"+> `expects` called once+> ``` ### 4. Flexible Verification (Matchers) @@ -265,7 +271,7 @@ f `shouldBeCalled` any ``` -#### Verify with Conditions (`expect`)+#### Verify with Conditions (`when`) You can verify using "conditions (predicates)" instead of arbitrary values. Powerfully useful for types without `Eq` (like functions) or when checking partial matches.@@ -273,10 +279,16 @@ ```haskell -- Return False only if the argument starts with "error" f <- mock do- onCase $ expect (\s -> "error" `isPrefixOf` s) "start with error" ~> False+ 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_`.++```haskell+f <- mock (when_ (> 5) ~> True)+```+ ### 5. Advanced Features - [Advanced] #### mock vs stub vs mockM@@ -355,23 +367,43 @@ | Matcher | Description | Example | | :--- | :--- | :--- | | `any` | Any value | `any ~> True` |-| `expect pred label` | Condition | `expect (>0) "positive" ~> True` |-| `expect_ pred` | No label | `expect_ (>0) ~> True` |+| `when pred label` | Condition | `when (>0) "positive" ~> True` |+| `when_ pred` | No label | `when_ (>0) ~> True` | ### Declarative Verification DSL (`expects`) -In `expects` blocks, you can describe expectations declaratively.-The syntax used in `expects` shares the same vocabulary as `shouldBeCalled`.+In `expects` blocks, you can describe expectations declaratively using a builder-style syntax.+It shares the same vocabulary as `shouldBeCalled`. -| Syntax | Description |-| :--- | :--- |-| `called` | Start expectation |-| `once` | Called exactly once |-| `times n` | Called n times |-| `never` | Never called |-| `with arg` | Expected argument |-| `with matcher` | Argument verification with matcher |+#### Basic Usage +Start with `called` and chain conditions.++```haskell+-- 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) |+ ### FAQ <details>@@ -410,6 +442,16 @@ 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.++```haskell+import Test.MockCat hiding (when)+-- or+import Control.Monad hiding (when) -- if you want to use the matcher ``` ### Ambiguous types with `OverloadedStrings`
mockcat.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: mockcat-version: 1.3.0.0+version: 1.3.1.0 synopsis: Declarative mocking with a single arrow `~>`. description: Mockcat is a minimal, architecture-agnostic mocking library for Haskell. It enables declarative verification and intent-driven matching, allowing you to define function behavior and expectations without specific architectural dependencies.
src/Test/MockCat/Param.hs view
@@ -21,6 +21,9 @@ param, ConsGen(..), MockSpec(..),+ -- * Matchers+ when,+ when_, expect, expect_, ToParamParam(..),@@ -241,6 +244,7 @@ > expect (>5) ">5" -}+{-# DEPRECATED expect "Use 'when' instead" #-} expect :: (a -> Bool) -> String -> Param a expect = ExpectCondition @@ -249,8 +253,26 @@ > expect_ (>5) -}+{-# DEPRECATED expect_ "Use 'when_' instead" #-} expect_ :: (a -> Bool) -> Param a expect_ f = ExpectCondition f "[some condition]"++{- | Create a conditional parameter with a label.+ When calling a mock function, if the argument does not satisfy this condition, an error occurs.+ In this case, the specified label is included in the error message.++ > when (>5) ">5"+-}+when :: (a -> Bool) -> String -> Param a+when = ExpectCondition++{- | Create a conditional parameter without a label.+ The error message is displayed as "[some condition]".++ > when_ (>5)+-}+when_ :: (a -> Bool) -> Param a+when_ f = ExpectCondition f "[some condition]" -- | The type of the argument parameters of the parameters. type family ArgsOf params where
src/Test/MockCat/TH.hs view
@@ -88,7 +88,7 @@ ) import Test.MockCat.TH.Types (MockOptions(..), options) import Test.MockCat.Verify ()-import Test.MockCat.Param+import Test.MockCat.Param hiding (when) import Prelude as P
test/Property/AdditionalProps.hs view
@@ -5,6 +5,7 @@ {-# OPTIONS_GHC -Wno-name-shadowing #-} {-# OPTIONS_GHC -Wno-type-defaults #-} {-# OPTIONS_GHC -fno-hpc #-}+{-# OPTIONS_GHC -Wno-deprecations #-} module Property.AdditionalProps ( prop_predicate_param_match_counts , prop_multicase_progression
test/Property/ReinforcementProps.hs view
@@ -9,6 +9,7 @@ {-# OPTIONS_GHC -Wno-missing-export-lists #-} {-# OPTIONS_GHC -Wno-simplifiable-class-constraints #-} {-# OPTIONS_GHC -fno-hpc #-}+{-# OPTIONS_GHC -Wno-deprecations #-} module Property.ReinforcementProps where
test/ReadmeVerifySpec.hs view
@@ -11,10 +11,11 @@ {-# OPTIONS_GHC -Wno-missing-export-lists #-} {-# OPTIONS_GHC -Wno-unused-do-bind #-} {-# OPTIONS_GHC -fno-hpc #-}+{-# OPTIONS_GHC -Wno-deprecations #-} module ReadmeVerifySpec where import Test.Hspec-import Test.MockCat+import Test.MockCat hiding (when) import Prelude hiding (readFile, writeFile, any) import Control.Monad (when)
test/Test/MockCat/ExampleSpec.hs view
@@ -10,6 +10,7 @@ {-# OPTIONS_GHC -Wno-name-shadowing #-} {-# OPTIONS_GHC -Wno-simplifiable-class-constraints #-} {-# OPTIONS_GHC -Wno-orphans #-}+{-# OPTIONS_GHC -Wno-deprecations #-} module Test.MockCat.ExampleSpec (spec) where
test/Test/MockCat/MockSpec.hs view
@@ -8,6 +8,7 @@ {-# OPTIONS_GHC -Wno-type-defaults #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wno-deprecations #-} {- HLINT ignore "Use newtype instead of data" -} module Test.MockCat.MockSpec (spec) where@@ -67,8 +68,8 @@ f <- mock $ any @NoEq ~> "result" f (NoEq "val") `shouldBe` "result" - it "can mock function with NoEq argument using expect" do- f <- mock $ expect_ (const True) ~> "result"+ it "can mock function with NoEq argument using when" do+ f <- mock $ when_ (const True) ~> "result" f (NoEq "val") `shouldBe` "result" it "can stub function with NoEq argument" do@@ -76,15 +77,25 @@ f (NoEq "val") `shouldBe` "result" it "can mock function with NoEq argument matching its value" do- -- Even without an Eq instance, you can match based on field values using 'expect'.- f <- mock $ expect (\(NoEq val) -> val == "target") "NoEq with 'target'" ~> "hit"+ -- Even without an Eq instance, you can match based on field values using 'when'.+ f <- mock $ when (\(NoEq val) -> val == "target") "NoEq with 'target" ~> "hit" f (NoEq "target") `shouldBe` "hit" evaluate (f (NoEq "other")) `shouldThrow` anyErrorCall it "can mock function taking a function" do- f <- mock $ expect_ (\(g :: Int -> Int) -> g (5 :: Int) == (25 :: Int)) ~> "result"+ f <- mock $ when_ (\(g :: Int -> Int) -> g (5 :: Int) == (25 :: Int)) ~> "result" let g x = x * x f g `shouldBe` "result"++ describe "Deprecated expect support" do+ it "expect works like when" do+ f <- mock $ expect (\(NoEq val) -> val == "target") "NoEq with 'target'" ~> "hit"+ f (NoEq "target") `shouldBe` "hit"+ evaluate (f (NoEq "other")) `shouldThrow` anyErrorCall++ it "expect_ works like when_" do+ f <- mock $ expect_ (const True) ~> "result"+ f (NoEq "val") `shouldBe` "result" describe "Monad" do it "Return IO Monad." do
test/Test/MockCat/ParamSpec.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE BlockArguments #-} {-# OPTIONS_GHC -Wno-type-defaults #-} {-# LANGUAGE TemplateHaskell #-}+{-# OPTIONS_GHC -Wno-deprecations #-} module Test.MockCat.ParamSpec (spec) where import Prelude hiding (and, or)
test/Test/MockCat/ShouldBeCalledSpec.hs view
@@ -3,6 +3,7 @@ {-# OPTIONS_GHC -Wno-type-defaults #-} {-# OPTIONS_GHC -Wno-unused-do-bind #-} {-# OPTIONS_GHC -fno-hpc #-}+{-# OPTIONS_GHC -Wno-deprecations #-} module Test.MockCat.ShouldBeCalledSpec (spec) where