hspec / hspec-hedgehog

A library to integrate hedgehog tests into your hspec test suite.
https://hackage.haskell.org/package/hspec-hedgehog
Other
28 stars 6 forks source link

How is this used with yesod-test? #42

Open jezen opened 3 months ago

jezen commented 3 months ago

Given I have some typical yesod-test scaffolding like this

withApp :: SpecWith (TestApp App) -> Spec
withApp = around (bracket beforeEach afterEach) . aroundWith (. fst)
  where
  beforeEach :: IO (TestApp App, ())
  beforeEach = do
    app <- makeFoundation ":memory:"
    pure ((app, defaultMiddlewaresNoLogging), ())

  afterEach :: (TestApp App, ()) -> IO ()
  afterEach ((app, _), _) = shutdownApp app

…how do I run yesod tests with hspec-hedgehog?

I'd like to write something like this (which, of course, doesn't work).

spec :: Spec
spec = withApp $ do

  it "Displays the param" $ hedgehog $ do
    -- for any given number…
    xs <- forAll $ Gen.int (Range.linear 0 1000)
    -- navigate to some page with that number
    get $ SomeRouteR xs
    -- assert that the page renders the number
    htmlAnyContain "p" xs
parsonsmatt commented 3 months ago

I wrote Effectful Property Testing which has my approach for it - but that has the assumption that you're doing some (forall x. m x -> IO x) inside of the test.

You can use a lambda to get the TestApp App -

spec = withApp do
  it "gimme" \testAppApp -> do
    -- now i'm in IO
    arrange (runYesodExample testAppApp) do
      -- now i'm writing a property
      xs <- forAll $ Gen.wahtever
      act do
        get $ SomeRouteR xs 
        htmlAnyContain "p" xs
        assert do pure ()

But unfortunately with yesod-test the code to do runYesodExample isn't exposed - you'd need to vendor it.

You may be interested in hspec-yesod, which I wrote to be a little more friendly to hspec idioms. I actually don't expose a runYesodExample function either, but the instance Example (YesodExample site) is much simpler than the one in yesod-test, and I'd be happy to extract that to a top-level named function to make it easier. I designed it for an almost 1:1 API correspondence so we could migrate our test suite over.

jezen commented 3 months ago

Thank you for the prompt and detailed response!

But unfortunately with yesod-test the code to do runYesodExample isn't exposed

Are you referring to evalSIO?

Separately, I take it that the last line in your example isn't actually needed?

parsonsmatt commented 3 months ago

evalSIO is the first part, but you also need to construct a YesodExampleData site from your TestApp site. It's not too hard to vendor the instance Example (SIO (YesodExampleData site) _) code to get a TestApp site -> YesodExample site a -> IO a function though.

If you have the more specific typed functions arrange and act then you do need to return a pure () (or other === assertions) - they're typed like PropertyT IO (YesodExample site (PropertyT IO ())).