hspec-effectful 1.1.0 → 1.1.1
raw patch · 4 files changed
+84/−20 lines, 4 filesdep +quickcheck-effectfuldep ~QuickCheckPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: quickcheck-effectful
Dependency ranges changed: QuickCheck
API changes (from Hackage documentation)
+ Effectful.Hspec: modifyArgs :: forall (es :: [Effect]) a. (HasCallStack, Hspec :> es) => (Args -> Args) -> Eff es a -> Eff es a
+ Effectful.Hspec: modifyMaxDiscardRatio :: forall (es :: [Effect]) a. (HasCallStack, Hspec :> es) => (Int -> Int) -> Eff es a -> Eff es a
+ Effectful.Hspec: modifyMaxShrinks :: forall (es :: [Effect]) a. (HasCallStack, Hspec :> es) => (Int -> Int) -> Eff es a -> Eff es a
+ Effectful.Hspec: modifyMaxSize :: forall (es :: [Effect]) a. (HasCallStack, Hspec :> es) => (Int -> Int) -> Eff es a -> Eff es a
+ Effectful.Hspec: modifyMaxSuccess :: forall (es :: [Effect]) a. (HasCallStack, Hspec :> es) => (Int -> Int) -> Eff es a -> Eff es a
- Effectful.Hspec: fprop :: forall prop (es :: [Effect]). (HasCallStack, Testable prop, Hspec :> es) => String -> prop -> Eff es ()
+ Effectful.Hspec: fprop :: forall prop (es :: [Effect]). (HasCallStack, Testable prop es, Hspec :> es) => String -> prop -> Eff es ()
- Effectful.Hspec: prop :: forall prop (es :: [Effect]). (HasCallStack, Testable prop, Hspec :> es) => String -> prop -> Eff es ()
+ Effectful.Hspec: prop :: forall prop (es :: [Effect]). (HasCallStack, Testable prop es, Hspec :> es) => String -> prop -> Eff es ()
- Effectful.Hspec: xprop :: forall prop (es :: [Effect]). (HasCallStack, Testable prop, Hspec :> es) => String -> prop -> Eff es ()
+ Effectful.Hspec: xprop :: forall prop (es :: [Effect]). (HasCallStack, Testable prop es, Hspec :> es) => String -> prop -> Eff es ()
Files
- CHANGELOG.md +8/−0
- hspec-effectful.cabal +9/−3
- src/Effectful/Hspec.hs +53/−17
- test/Main.hs +14/−0
CHANGELOG.md view
@@ -5,6 +5,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/). +## [1.1.1] - 2026-07-26++### Added++- `modify*` QuickCheck combinators+- Dependency on `quickcheck-effectful`+- Ability to run effectful computation in QuickCheck props+ ## [1.1.0] - 2026-07-21 ### Added
hspec-effectful.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: hspec-effectful-version: 1.1.0+version: 1.1.1 synopsis: Effectful driver for Hspec category: Test description:@@ -26,6 +26,8 @@ -Wno-missing-import-lists -Wno-missing-kind-signatures -Wno-all-missed-specialisations+ -Wno-missing-role-annotations+ -Wno-term-variable-capture default-extensions: ApplicativeDo@@ -40,6 +42,7 @@ FlexibleContexts GeneralizedNewtypeDeriving ImportQualifiedPost+ ImpredicativeTypes LambdaCase NamedFieldPuns NoImplicitPrelude@@ -48,6 +51,7 @@ OverloadedStrings RecordWildCards RecursiveDo+ ScopedTypeVariables TypeApplications TypeFamilies TypeOperators@@ -56,12 +60,13 @@ build-depends: base >=4.16 && <5, effectful >=2.6 && <2.7,+ quickcheck-effectful >=1.0 && <1.1, library import: common hs-source-dirs: src build-depends:- QuickCheck >=2.12 && <3,+ QuickCheck >=2.15 && <3, hspec >=2.11 && <2.12, hspec-core >=2.11 && <2.12, hspec-expectations >=0.8 && <0.9,@@ -78,4 +83,5 @@ hs-source-dirs: test main-is: Main.hs build-depends:- hspec-effectful+ hspec-effectful,+ quickcheck-effectful,
src/Effectful/Hspec.hs view
@@ -1,8 +1,4 @@-{-# LANGUAGE ImpredicativeTypes #-} {-# LANGUAGE Trustworthy #-}-{-# OPTIONS_GHC -Wno-missing-role-annotations #-}-{-# OPTIONS_GHC -Wno-orphans #-}-{-# OPTIONS_GHC -Wno-term-variable-capture #-} -- | -- Module : Effectful.Hspec@@ -37,16 +33,16 @@ -- > put $ funds - amount -- > pure True ----- Use 'describe' to group related examples, 'it' to state individual expectations--- and setup/teardown combinators such as 'before_', 'after_', or 'around_' to+-- Use 'describe' to group related examples, 'prop' and 'it' to state individual properties or+-- expectations ,and setup/teardown combinators such as 'before_', 'after_', or 'around_' to -- run the examples from a known initial state: -- -- > accountSpec :: (State Int :> es, Hspec :> es) => Eff es () -- > accountSpec = describe "account" . before_ (put @Int 0) $ do--- > it "accumulates deposits" $ do--- > deposit 100--- > deposit 50--- > balance `shouldReturn` 150+-- > prop "accumulates deposits" \(Positive a) (Positive b) -> do+-- > deposit a+-- > deposit b+-- > balance `shouldReturn` a + b -- > -- > it "checks for sufficient funds before withdrawing" $ do -- > deposit 100@@ -116,6 +112,11 @@ , anyException -- * Properties+ , modifyArgs+ , modifyMaxSuccess+ , modifyMaxDiscardRatio+ , modifyMaxSize+ , modifyMaxShrinks , prop , xprop , fprop@@ -133,7 +134,9 @@ import Effectful.Dispatch.Static import Effectful.Environment (runEnvironment, withArgs) import Effectful.Exception (Exception, throwIO, try)-import Effectful.HUnit as HUnit+import Effectful.HUnit (Assertion, HUnit, runHUnit, (@?=))+import Effectful.HUnit qualified as HUnit+import Effectful.QuickCheck (Testable, property, unliftProperty) import Test.Hspec qualified as Hspec import Test.Hspec.Core.Spec (ResultStatus (..)) import Test.Hspec.Expectations (Selector, anyException)@@ -430,14 +433,47 @@ instanceOf :: Selector a -> a instanceOf _ = error "Effectful.Hspec.shouldThrow: broken Typeable instance" +-- | Lifted 'Hspec.modifyArgs'.+modifyArgs+ :: (HasCallStack, Hspec :> es)+ => (QuickCheck.Args -> QuickCheck.Args)+ -> Eff es a+ -> Eff es a+modifyArgs = mapSpec . Hspec.modifyArgs++-- | Lifted 'Hspec.modifyMaxSuccess'.+modifyMaxSuccess :: (HasCallStack, Hspec :> es) => (Int -> Int) -> Eff es a -> Eff es a+modifyMaxSuccess = mapSpec . Hspec.modifyMaxSuccess++-- | Lifted 'Hspec.modifyMaxDiscardRatio'.+modifyMaxDiscardRatio :: (HasCallStack, Hspec :> es) => (Int -> Int) -> Eff es a -> Eff es a+modifyMaxDiscardRatio = mapSpec . Hspec.modifyMaxDiscardRatio++-- | Lifted 'Hspec.modifyMaxSize'.+modifyMaxSize :: (HasCallStack, Hspec :> es) => (Int -> Int) -> Eff es a -> Eff es a+modifyMaxSize = mapSpec . Hspec.modifyMaxSize++-- | Lifted 'Hspec.modifyMaxSize'.+modifyMaxShrinks :: (HasCallStack, Hspec :> es) => (Int -> Int) -> Eff es a -> Eff es a+modifyMaxShrinks = mapSpec . Hspec.modifyMaxShrinks++appendProp+ :: (HasCallStack, Testable prop es, Hspec :> es)+ => (QuickCheck.Property -> Hspec.Spec)+ -> prop+ -> Eff es ()+appendProp f p =+ appendSpec . f =<< unsafeConcUnliftIO Ephemeral Unlimited \unlift ->+ pure . unliftProperty unlift . property $ p+ -- | Lifted 'Hspec.prop'.-prop :: (HasCallStack, QuickCheck.Testable prop, Hspec :> es) => String -> prop -> Eff es ()-prop = (appendSpec .) . Hspec.prop+prop :: (HasCallStack, Testable prop es, Hspec :> es) => String -> prop -> Eff es ()+prop = appendProp . Hspec.prop -- | Lifted 'Hspec.xprop'.-xprop :: (HasCallStack, QuickCheck.Testable prop, Hspec :> es) => String -> prop -> Eff es ()-xprop = (appendSpec .) . Hspec.xprop+xprop :: (HasCallStack, Testable prop es, Hspec :> es) => String -> prop -> Eff es ()+xprop = appendProp . Hspec.xprop -- | Lifted 'Hspec.fprop'.-fprop :: (HasCallStack, QuickCheck.Testable prop, Hspec :> es) => String -> prop -> Eff es ()-fprop = (appendSpec .) . Hspec.fprop+fprop :: (HasCallStack, Testable prop es, Hspec :> es) => String -> prop -> Eff es ()+fprop = appendProp . Hspec.fprop
test/Main.hs view
@@ -1,11 +1,16 @@ {-# OPTIONS_GHC -Wno-missing-local-signatures #-} {-# OPTIONS_GHC -Wno-monomorphism-restriction #-}+{-# OPTIONS_GHC -Wno-name-shadowing #-} {-# OPTIONS_GHC -Wno-type-defaults #-} +{- HLINT ignore "Redundant reverse" -}+ module Main where import Effectful import Effectful.Hspec+import Effectful.QuickCheck ((.&&.), (===))+import Effectful.State.Static.Local (evalState, get, modify) import Prelude main :: IO ()@@ -49,3 +54,12 @@ it "handles shouldNotReturn" $ do let action = pure (1 :: Int) :: Eff es Int action `shouldNotReturn` 2++ prop "pure prop" \(xs :: [Int]) ->+ (reverse . reverse) xs == xs++ prop "eff prop" \(n :: Int) ->+ evalState n do+ modify @Int (+ 1)+ x <- get+ pure $ (x === n + 1) .&&. (x > n)