hspec / hspec-wai

Helpers to test WAI applications with Hspec

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Equivalent of withApplication with app wrapped in IO (e.g. for Scotty)

tomasaschan opened this issue · comments

Hello! Haskell newbie here :) I'm learning how to build a web API in Haskell, and I'm stumbling on figuring out the correct way to test my app. What I want to accomplish is basically to

  1. set some initial app state
  2. make an http call
  3. verify stuff about the response
  4. verify stuff about the state after the request returns

For now, I don't mind if the test runner has to run the entire cycle for each verification, so 3. and 4. could be lumped together into "verify something about the response or the app state after the action".

I've looked at the examples posted in #36 (as well as in a bunch of other places) but I'm only almost getting all the pieces in place to start writing real tests. The problem I'm currently facing is that in #36, the mkApplication is of type Connection -> Application, which seems to follow from the OP there using Servant. I'm using Scotty, where my corresponding function type is MyState -> IO Application, and I can't figure out how to make that IO match up with the stuff around it.

Here's my attempt at defining a Spec:

mkState :: IO (TVar MyState)
mkState = liftIO $ newTVarIO (def :: MyState)

mkApp :: TVar MyState -> IO Network.Wai.Application
mkApp s = scottyAppT (runWithState s) myApp
  where
    -- WebM is defined as here: https://github.com/scotty-web/scotty/blob/master/examples/globalstate.hs
    runWithState :: TVar MyState -> WebM a -> IO a
    runWithState s = \m -> runReaderT (runWebM m) s

spec :: Spec
spec = do
  before mkState $ do
    it "get /" $ \s -> do
      withApplication (mkApp s) $ do 
        Test.Hspec.Wai.pending -- e.g. get "/" `shouldRespondWith` 200
                               -- or do s' <- readTVar s
                                        getStuff s `shouldBe` expectedStuff

The above snippet fails to build because mkApp s is expected to return an Application, but returns an IO Application. I bet I'm missing something fairly trivial, but I haven't been able to figure out what.

Grateful for all help!

Here's a different sample, that also fails to build:

spec :: Spec
spec = do
  before mkSt $ do
    it "get /" $ \s -> do 
      with (mkApp s) $ do 
        get "/" `shouldRespondWith` 200

This time, it's the last line (get "/" ...) that fails, because it is of type WaiExpectation and not SpecWith Application.