I have an in-memory repository that I can create by calling this function:
newEmptyRepository :: IO InMemoryGameRepository
where InMemoryGameRepository
is defined like this:
type State = (HashMap GameId Game)
type IORefState = IORef State
newtype InMemoryGameRepository = InMemoryGameRepository IORefState
When writing tests for my Scotty application I've seen examples of using this approach:
spec =
before app $ do
describe "GET /" $ do
it "responds with 200" $ get "/" `shouldRespondWith` 200
it "responds with 'hello'" $ get "/" `shouldRespondWith` "hello"
...
This is all fine but I need to somehow also initialize the InMemoryGameRepository (by calling newEmptyRepository
) and use the created instance in my tests. Thus I've changed app
to:
app :: InMemoryGameRepository -> IO Application
app repo = scottyApp $ routes repo
And I'm trying to create a test that uses the repository AND the IO Application
, for example like this (which doesn't work):
spec =
before (do repo <- newEmptyRepository
app repo) $
-- API Tests
describe "GET /api/games" $
it "responds with " $ do
liftIO $ startGame repo
get "/api/games" `shouldRespondWith` singleGameResponse
where startGame
is defined like this:
startGame :: InMemoryGameRepository -> IO Game
Here the compiler says (obviously) that repo
is not in scope. But how can I achieve this? I.e. I want to share a single instance of newEmptyRepository
both for the app
and in the test?
Ps: you can see the full application on github.
You should use beforeWith which has the type
Use it as e.g.
before newEmptyRepository . beforeWith app
whose type isSpecWith Application -> Spec
.If you want to access both the
InMemoryGameRepository
and theApplication
in your test cases, defined a helper functionthen use
Finally, you shouldn't use
liftIO $ startGame repo
in the definition of your tests - this runsstartGame
every time the test tree is built (although, this may actually be what you want, it doesn't seem to be the case). Instead, if you use thebefore
family of functions,startGame
will run once before the tests are actually run. You can even access theGame
returned bystartGame
using the same technique as above: