Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration with hspec #41

Open
chshersh opened this issue Jan 9, 2022 · 3 comments
Open

Integration with hspec #41

chshersh opened this issue Jan 9, 2022 · 3 comments

Comments

@chshersh
Copy link
Contributor

chshersh commented Jan 9, 2022

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 🙂

@chessai
Copy link
Collaborator

chessai commented Dec 17, 2022

I would take a PR for this.

@sol
Copy link
Contributor

sol commented Nov 13, 2023

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)

sol added a commit to sol/haskell-hedgehog-classes that referenced this issue Nov 13, 2023
instead of "Reflexive", "Symmetric" and "Transitive".

This is consistent with how `semigroupLaws` and `monoidLaws` use
"Associativity" and "Identity".

It is also consistent with the terminology used in the Haddocks of
`base`:
https://hackage.haskell.org/package/base-4.19.0.0/docs/Data-Eq.html#t:Eq

My practical motivation for this change is that it will make the output
for `hspec` consistent and grammatically correct. (see
hedgehogqa#41 (comment))
chessai pushed a commit that referenced this issue Nov 15, 2023
instead of "Reflexive", "Symmetric" and "Transitive".

This is consistent with how `semigroupLaws` and `monoidLaws` use
"Associativity" and "Identity".

It is also consistent with the terminology used in the Haddocks of
`base`:
https://hackage.haskell.org/package/base-4.19.0.0/docs/Data-Eq.html#t:Eq

My practical motivation for this change is that it will make the output
for `hspec` consistent and grammatically correct. (see
#41 (comment))
@chessai
Copy link
Collaborator

chessai commented Jan 21, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants