haskell-hedgehog-classes
haskell-hedgehog-classes copied to clipboard
Integration with hspec
It would be nice if hedgehog-classes
could be integrated with other tasting frameworks, e.g. hspec
.
Currently, when I run lawsCheck
inside the hspec
test suite, I see the following slightly messy output:
Tests
Lecture 3
Gold
Laws: Semigroup [ ]Semigroup: Associativity ✓ <interactive> passed 100 tests.
Semigroup: Concatenation ✓ <interactive> passed 100 tests.
Semigroup: Times ✓ <interactive> passed 100 tests.
Laws: Semigroup [✔]
Laws: Monoid [ ]Monoid: Left Identity ✓ <interactive> passed 100 tests.
Monoid: Right Identity ✓ <interactive> passed 100 tests.
Monoid: Associativity ✓ <interactive> passed 100 tests.
Monoid: Concatenation ✓ <interactive> passed 100 tests.
Laws: Monoid [✔]
Here is my relevant test-suite code:
describe "Gold" $ do
it "Laws: Semigroup" $
lawsCheck (semigroupLaws genGold) `shouldReturn` True
it "Laws: Monoid" $
lawsCheck (monoidLaws genGold) `shouldReturn` True
I see that hedgehog
already has integration with hspec
in the form of hspec-hedgehog package. The integration is done my providing relevant instances to the PropertyT
data type.
So, I wonder, if hedgehog-classes
can expose PropertyT
of the underlying laws to be used with hspec
🤔
Or in any other way 🙂
I would take a PR for this.
This can be done with
import Test.Hspec
import Test.Hspec.Hedgehog
import Control.Monad
import Hedgehog.Gen qualified as Gen
import Hedgehog.Range qualified as Range
import Hedgehog.Classes
import Hedgehog.Internal.Property
satisfies :: Gen a -> (Gen a -> Laws) -> Spec
satisfies gen laws = do
describe (className <> " instance") $ do
forM_ properties $ \ (name, p) -> do
it ("satisfies " <> name) (propertyTest p)
where
Laws className properties = laws gen
which then can be used with e.g.:
main :: IO ()
main = hspec spec
arbitraryString :: Gen [Char]
arbitraryString = Gen.string (Range.constant 0 10) (Gen.unicodeAll)
spec :: Spec
spec = do
describe "String" $ do
arbitraryString `satisfies` eqLaws
arbitraryString `satisfies` monoidLaws
(note that this ignores the propertyConfig
and instead uses the settings provided by hspec
)
Also, for satisfies1
, I think you can do the following:
-- needs AllowAmbiguousTypes, TypeApplications, ScopedTypeVariables, QuantifiedConstraints, RankNTypes
satisfies1 :: forall c f. (c f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x))
=> (forall a. Gen a -> Gen (f a))
-> ((c f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => (forall x. Gen x -> Gen (f x)) -> Laws)
-> Spec
satisfies1 gen laws = case laws gen of
Laws className properties -> do
describe (className <> " instance") $ do
forM_ properties $ \(name, p) -> do
it ("satisfies " <> name) (propertyTest p)
genList :: Gen a -> Gen [a]
genList = Gen.list (Range.linear 0 6)
foo :: Spec ()
foo = satisfies1 @Applicative @[] genList applicativeLaws
and you could basically do the same thing for satisfies2.