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 appwhose type isSpecWith Application -> Spec.If you want to access both the
InMemoryGameRepositoryand theApplicationin your test cases, defined a helper functionthen use
Finally, you shouldn't use
liftIO $ startGame repoin the definition of your tests - this runsstartGameevery 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 thebeforefamily of functions,startGamewill run once before the tests are actually run. You can even access theGamereturned bystartGameusing the same technique as above: