{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreiewzfafd74rm352i5vnie5jhksda2gkpogrokd63nqyz364trioka",
"uri": "at://did:plc:pi6woz4d47bkuws673w2il2r/app.bsky.feed.post/3mj7ji5drg2o2"
},
"path": "/t/rfc-hspec-quickcheck-classes-testing-typeclass-laws-from-hspec/13919#post_1",
"publishedAt": "2026-04-11T08:44:32.000Z",
"site": "https://discourse.haskell.org",
"tags": [
"hspec-quickcheck-classes",
"Hspec",
"quickcheck-classes",
"Laws",
"Spec",
"monoidmap",
"LeftReductive",
"documentation",
"implementation",
"@Foo",
"@Maybe",
"@Either"
],
"textContent": "I’ve created a small library called hspec-quickcheck-classes, for testing typeclass laws within Hspec specifications.\n\nIt’s a tiny library (with just **one** function), but I’d be very grateful for any feedback before I release it on Hackage.\n\n## What problem does it solve?\n\nThis library integrates Hspec with quickcheck-classes. For those not familiar:\n\n * Hspec is a very popular Haskell testing framework that provides a readable DSL for structuring tests.\n\n * quickcheck-classes is a library that provides QuickCheck property tests for the laws of typeclasses such as `Eq`, `Ord`, `Functor`, `Applicative`, `Monad`, and many others. It defines a Laws data type, which bundles together a list of named properties for a single typeclass.\n\n\n\n\nUsing `quickcheck-classes` within an `Hspec` specification requires a small but repetitive amount of **boilerplate**.\n\nThis library handles this boilerplate for you, constructing a Spec from a given type and a set of Laws. The generated `Spec` consists of a tree with an outer node for the type, an inner node for each typeclass, and a leaf for each law being tested (where each law has a corresponding QuickCheck property):\n\n\n <type> ← outer node (type)\n ├── <typeclass #a> ← inner node (typeclass)\n │ ├── <law #1> ← leaf (law)\n │ ├── <law #2>\n │ └── ...\n ├── <typeclass #b>\n │ ├── <law #1>\n │ ├── <law #2>\n │ └── ...\n └── <typeclass #c>\n ├── <law #1>\n ├── <law #2>\n └── ...\n ...\n\n\n## The API\n\nThe library exposes just a single function:\n\n\n testLaws\n :: forall a. (Typeable a, HasCallStack)\n => [Proxy a -> Laws]\n -> Spec\n\n\nUsage looks like this:\n\n\n import Test.Hspec\n ( hspec )\n import Test.Hspec.QuickCheck.Classes\n ( testLaws )\n import Test.QuickCheck\n ( Arbitrary (..) )\n import Test.QuickCheck.Classes\n ( eqLaws, ordLaws, showLaws )\n\n -- Import the data type you'd like to test:\n import Data.Foo\n ( Foo (..) )\n\n -- Define (or import) an 'Arbitrary' instance for your data type:\n instance Arbitrary Foo where\n arbitrary = ...\n shrink = ...\n\n main :: IO ()\n main = hspec $ do\n\n -- Test that your data type obeys the laws of 'Eq', 'Ord', and 'Show':\n testLaws @Foo\n [ eqLaws\n , ordLaws\n , showLaws\n ]\n\n\nThis produces output along the lines of:\n\n\n Testing laws for Foo\n Eq\n Transitive [✔]\n +++ OK, passed 100 tests.\n Symmetric [✔]\n +++ OK, passed 100 tests.\n Reflexive [✔]\n +++ OK, passed 100 tests.\n Ord\n Antisymmetry [✔]\n +++ OK, passed 100 tests.\n Transitivity [✔]\n +++ OK, passed 100 tests.\n Totality [✔]\n +++ OK, passed 100 tests.\n Show\n Show [✔]\n +++ OK, passed 100 tests.\n Equivariance: showsPrec [✔]\n +++ OK, passed 100 tests.\n Equivariance: showList [✔]\n +++ OK, passed 100 tests.\n\n\nOn **failure** , the output includes a **counterexample** , and the **location** of the test that failed in the test source code.\n\nFor example, here’s a failing test taken from the monoidmap package, showing a violation of the LeftReductive laws (due to a deliberately-introduced bug):\n\n\n test/Data/MonoidMap/Internal/ClassSpec.hs:92:9:\n 1) Data.MonoidMap, Testing laws for MonoidMap String String, LeftReductive, leftReductiveLaw_stripPrefix\n Falsified (after 20 tests and 10 shrinks):\n Property not satisfied:\n maybe b (a <>) (stripPrefix a b) == b\n Values:\n a =\n fromList []\n b =\n fromList [(\"A\",\"a\")]\n stripPrefix a b =\n Just (fromList [(\"A\",\"a\")])\n maybe b (a <>) (stripPrefix a b) =\n fromList []\n\n\n### Kind polymorphism\n\nTo support testing of laws for higher-kinded classes like `Functor`, `Applicative`, and `Traversable`, the `testLaws` function is kind-polymorphic, with no special casing required.\n\nFor example, with `Maybe`, which has kind `Type -> Type`:\n\n\n testLaws @Maybe\n [ applicativeLaws\n , functorLaws\n , monadLaws\n , foldableLaws\n , traversableLaws\n ]\n\n\nAnd `Either`, which has kind `Type -> Type -> Type`:\n\n\n testLaws @Either\n [ bifoldableLaws\n , bifunctorLaws\n , bitraversableLaws\n ]\n\n\n## Feedback\n\nObviously this is a tiny library, but if anyone has any feedback they’d like to share about the API, the documentation, or the implementation, I’d be very grateful to hear your thoughts before the library is published.\n\nMany thanks for reading!",
"title": "[RFC] hspec-quickcheck-classes: testing typeclass laws from Hspec"
}