Open-source project
hedgehogqa/haskell-hedgehog avatar
hedgehogqa/haskell-hedgehog

hedgehog: property-based testing with shrinking built into Haskell

Release with confidence, state-of-the-art property testing for Haskell.

701 stars113 forksHaskellLicense varies

At a glance

What is it?
hedgehog is a property-based testing library for Haskell that generates test cases in bulk, shrinks failures down to a minimal example, and runs properties concurrently through a Template Haskell splice. Its range combinators and abstract state machine testing are the parts that go beyond a QuickCheck style workflow, and its shrinking is integrated rather than bolted on.
Who is it for?
hedgehog fits a Haskell team that wants generated inputs, minimal failing examples and concurrent property runs inside its existing cabal project, and it does not fit a team that cannot enable Template Haskell or that only needs a handful of hand written cases. Before adopting it, add the hedgehog package to a cabal.project, run the reverse property from the README in GHCi, and confirm that your build accepts a Template Haskell splice.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly Haskell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Hundreds of cases, then a failure small enough to read

hedgehog is a property-based testing library for Haskell, and the README describes it as automatically generating a comprehensive array of test cases, exercising software in ways human testers would never imagine, generating hundreds of test cases automatically to expose corner cases, and simplifying failures automatically so developers get coherent, intelligible error messages.

That is the whole proposition, and it is a different bargain from writing example-based tests. You state a property, such as that reversing a list twice gives the original back, and the library hunts for the input that breaks it. The value is concentrated in the failure case: the report tells you which generated value caused the problem, and that value is small enough to paste into a regression test.

The audience is Haskell developers who already write property tests or want to. The topics list property-based-testing, property-testing, quickcheck and test alongside haskell, which places it in the same family as QuickCheck, and the repository layout shows the family is literal: alongside the main hedgehog directory sit hedgehog-example, hedgehog-quickcheck, hedgehog-corpus, hedgehog-dieharder and hedgehog-test-laws. That is a library package with companion packages for examples, QuickCheck interoperability, corpus support, dieharder integration and law checking, all in one repository.

The README also carries a memorial note for Jacob Stanley, an author and co-founder of the project, who passed away on April 9th. The project states that it continues to be maintained and developed in his memory.

Shrinking is integrated, so shrinks keep the invariants

The first feature in the README's list is integrated shrinking, with the qualifier that shrinks obey invariants by construction. That phrase carries the design.

In a generator-based test, a shrink is a way to take a failing value and produce a smaller one, and the hope is that the smaller value still fails, which points at the actual cause. The problem is that shrinking naively breaks the properties the generator was built to respect. A generator for a non-empty list, for example, cannot shrink to the empty list, and a generator for a valid email address cannot shrink to an invalid one. If the shrink ignores those constraints, the reported counterexample is either invalid or the shrinking wanders into values that no longer represent the bug.

Building the shrink into the generator means the smaller values are drawn from the same constrained space as the original, so a shrunk counterexample is always a legitimate input. That is a stronger guarantee than adding a shrink pass afterwards, and it is why the README lists it first rather than as an afterthought.

The other features build on the same premise. Equality and roundtrip assertions show a diff instead of the two inequal values, so the failure output shows what changed rather than two opaque blobs. Generators allow monadic effects, which lets a generator depend on state, such as drawing a value that is valid in the context it will be used in. Range combinators give full control over the scope of generated numbers and collections.

The Template Haskell splice finds your properties

Properties do not have to be registered by hand. The recommended path is a splice.

hs
tests :: IO Bool
tests =
  checkParallel $$(discover)

The discover splice walks the module for values of type Property and collects them, and checkParallel runs them concurrently. The README is explicit that you need to enable Template Haskell so the runner can find your properties, which is why the imports block starts with the language pragma.

hs
{-# LANGUAGE TemplateHaskell #-}

import           Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range

If you would rather avoid macros, the README gives the manual form, where the group of properties is written out by name. The group label is what appears in the test output.

hs
{-# LANGUAGE OverloadedStrings #-}

tests :: IO Bool
tests =
  checkParallel $ Group "Test.Example" [
      ("prop_reverse", prop_reverse)
    ]

Two practical points. The concurrency in checkParallel means properties run in parallel, so two properties that interfere with each other through shared state will misbehave in ways a serial runner would not, and the README lists concurrent execution as a feature rather than a caveat. And the discover approach means a property added to a module is picked up automatically, which removes the bookkeeping but also removes the explicit list you might use to decide what runs in CI.

Generators and ranges split shape from scope

The API is built on two modules that the README says should be imported qualified, Hedgehog.Gen and Hedgehog.Range, alongside the main Hedgehog module that includes almost everything needed to get started. The split is the interesting part, because the two modules answer different questions.

A generator decides the shape of a value: a list, a character, a pair, a value drawn from a range. A range decides the scope: a lower and an upper bound. Keeping them apart means the same generator can be reused at different sizes, and a test can widen or narrow the scope without rewriting how the value is built.

The example property shows the composition, generating a list of alpha characters whose length is drawn from a linear range between 0 and 100.

hs
prop_reverse :: Property
prop_reverse =
  property $ do
    xs <- forAll $ Gen.list (Range.linear 0 100) Gen.alpha
    reverse (reverse xs) === xs

forAll draws a value from the generator and binds it, and the === operator states the property as an equality that Hedgehog can check and report with a diff. Because the list length is itself generated rather than fixed, the property is exercised at sizes the author did not think to enumerate, and the shrinker can then reduce a failing list to a minimal length.

The consequence for a reader is that the API has a small number of ideas that compose, and most of the work in writing a property is choosing generators that keep the interesting values in scope without producing so many cases that the test becomes slow.

Abstract state machine testing is the part with no easy substitute

The README lists abstract state machine testing as a separate feature, and it is the one that distinguishes this library from a general property testing approach.

The idea is that instead of testing one pure function over random inputs, you describe a system as a set of states and transitions, then let the library generate sequences of commands, run them against the model, and check that the real implementation and the model agree at every step. What makes it abstract is that the generator and the model live together, so the test author describes what should happen rather than writing a script of concrete steps.

The value shows up in code that has state and sequence dependent behaviour, where example based tests cover one happy path and property testing over a single function misses the ordering bugs. A state machine property can generate a long, adversarial sequence of operations and check an invariant after each one.

The cost is that this is the most machinery in the library. It asks a team to model the system a second time, and a model that is wrong in the same way as the implementation passes the test. The README does not document the API in detail, so the reference for that part is the Haddock pages it links for Hedgehog, Hedgehog.Gen and Hedgehog.Range, and the hedgehog-example and hedgehog-test-laws packages in the repository are where working shapes are most likely to live.

Adding hedgehog to a project and running the first property

hedgehog is distributed on Hackage as the hedgehog package, and the README points there through a version badge and the Haddock links. There is no install command in the README, so the route is the usual Haskell one: declare hedgehog as a dependency in the cabal file of your project, and the repository's cabal.project is the file that pins a set of packages together for a multi-package workspace.

The first property from the README is short enough to type in by hand, and it is worth running before you write anything of your own, because it exercises the whole path: the generator, the range, the equality assertion and the runner.

The three steps are the imports with Template Haskell enabled, the property itself, and the splice that discovers and runs it. After that the README says you can load the module in GHCi and run it, and shows the group banner followed by a line reporting that the property passed 100 tests. The 100 is the default number of cases for a property, and it is a small number relative to what you would want for a property that shrinks, so a property that passes on the first run is not strong evidence.

The repository also ships a STYLE_GUIDE.md alongside the CHANGELOG.md, which is worth reading before you commit to a style for how properties are written, and a .github directory with a Haskell CI workflow, so the same checks that run for the library can run for your project.

Hackage is the release channel, and the licence is not stated here

The repository has no GitHub releases, so version history is not visible on the project page, and the CHANGELOG.md at the top level is the record of what changed. The last push was on 2026-10-15 and the repository is not archived, so development is current even though the release history lives elsewhere.

That split matters for adoption. A team that pins a version is pinning a Hackage release, not a commit in this repository, and the README links the Hackage page for the package, the version badge and the Haddock API. For a library with a stable public API and a long history, that is normal Haskell practice, and it is also why the missing GitHub releases are not a red flag on their own.

The licence is the one loose end in the documentation. The README does not name one, and neither does the project description, so the terms are not available from the files described here. Checking the licence field on the Hackage package page before you vendor the code or ship it inside a product is the concrete next step, since a permissive or copyleft choice would put very different obligations on a commercial project.

Beyond that, the multi-package layout means the upgrade unit is a choice. Depending on the packages you use, a new hedgehog release may arrive with matching changes in hedgehog-quickcheck or hedgehog-test-laws, and the compatibility between those packages is the part to check when you bump a version.

Editorial conclusion

hedgehog fits a Haskell team that wants generated inputs, minimal failing examples and concurrent property runs inside its existing cabal project, and it does not fit a team that cannot enable Template Haskell or that only needs a handful of hand written cases. Before adopting it, add the hedgehog package to a cabal.project, run the reverse property from the README in GHCi, and confirm that your build accepts a Template Haskell splice.

Frequently asked questions

What is hedgehog used for in Haskell?

hedgehog is a property-based testing library. It automatically generates hundreds of test cases for a property you state, and simplifies failures automatically so the reported counterexample is small and readable.

How do I run hedgehog properties in a test module?

Enable Template Haskell, import Hedgehog, Hedgehog.Gen and Hedgehog.Range qualified, and add the splice checkParallel $$(discover) in an IO Bool binding. If you prefer to avoid macros, the README shows a manual Group form listing the properties by name.

What is the difference between Hedgehog.Gen and Hedgehog.Range?

Hedgehog.Gen provides generators that decide the shape of a generated value, while Hedgehog.Range provides range combinators that set the scope of generated numbers and collections. The README example combines them in Gen.list (Range.linear 0 100) Gen.alpha.

Does hedgehog support QuickCheck style tests?

The repository contains a hedgehog-quickcheck package alongside the main hedgehog package, along with hedgehog-example, hedgehog-corpus, hedgehog-dieharder and hedgehog-test-laws, so interoperability with QuickCheck and related tooling is part of the layout.

How many test cases does a hedgehog property run?

The README output example shows a property passing 100 tests, which is the default case count for a property. Nothing in the documentation changes that default, so a property that passes quickly is a weak signal until you widen it.

Official sources

  1. hedgehogqa/haskell-hedgehog on GitHub
  2. Issues
  3. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hedgehogqa-haskell-hedgehog.svg)](https://hysenlabs.com/projects/hedgehogqa-haskell-hedgehog)

Was this useful?