diff --git a/hspec-wai.cabal b/hspec-wai.cabal
--- a/hspec-wai.cabal
+++ b/hspec-wai.cabal
@@ -1,13 +1,11 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.31.0.
+-- This file has been generated from package.yaml by hpack version 0.39.1.
 --
 -- see: https://github.com/sol/hpack
---
--- hash: 8cd8444fb81ce20f9ac1bc6ef05bcaf0c7bfec1e15deefc045dbe71f1830a92b
 
 name:             hspec-wai
-version:          0.9.2
+version:          0.12.0
 homepage:         https://github.com/hspec/hspec-wai#readme
 bug-reports:      https://github.com/hspec/hspec-wai/issues
 license:          MIT
@@ -35,7 +33,7 @@
   ghc-options: -Wall
   build-depends:
       QuickCheck
-    , base ==4.*
+    , base >=4.9.1.0 && <5
     , base-compat
     , bytestring >=0.10
     , case-insensitive
@@ -45,7 +43,7 @@
     , text
     , transformers
     , wai >=3
-    , wai-extra >=3
+    , wai-extra >=3.1.14
   exposed-modules:
       Test.Hspec.Wai
       Test.Hspec.Wai.QuickCheck
@@ -63,9 +61,11 @@
       src
       test
   ghc-options: -Wall
+  build-tool-depends:
+      hspec-discover:hspec-discover
   build-depends:
       QuickCheck
-    , base ==4.*
+    , base >=4.9.1.0 && <5
     , base-compat
     , bytestring >=0.10
     , case-insensitive
@@ -75,8 +75,8 @@
     , http-types
     , text
     , transformers
-    , wai >=3
-    , wai-extra >=3
+    , wai >=3.2.2
+    , wai-extra >=3.1.14
   other-modules:
       Test.Hspec.Wai
       Test.Hspec.Wai.Internal
diff --git a/src/Test/Hspec/Wai.hs b/src/Test/Hspec/Wai.hs
--- a/src/Test/Hspec/Wai.hs
+++ b/src/Test/Hspec/Wai.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE PackageImports #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE ConstraintKinds #-}
@@ -32,14 +33,21 @@
 -- * Helpers and re-exports
 , liftIO
 , with
+, withState
+, getState
 , pending
 , pendingWith
+, SResponse(..)
 ) where
 
+import           Prelude ()
+import "base-compat" Prelude.Compat
+
 import           Data.Foldable
 import           Data.ByteString (ByteString)
 import qualified Data.ByteString.Lazy as LB
 import           Control.Monad.IO.Class
+import           Control.Monad.Trans.Class (lift)
 import           Network.Wai (Request(..))
 import           Network.HTTP.Types
 import           Network.Wai.Test hiding (request)
@@ -54,16 +62,20 @@
 import           Test.Hspec.Wai.Internal
 import           Test.Hspec.Wai.Matcher
 
--- | An alias for `before`.
-with :: IO a -> SpecWith a -> Spec
-with = before
+import           Network.Wai (Application)
 
+with :: IO Application -> SpecWith ((), Application) -> Spec
+with action = before ((,) () <$> action)
+
+withState :: IO (st, Application) -> SpecWith (st, Application) -> Spec
+withState = before
+
 -- | A lifted version of `Core.pending`.
-pending :: WaiSession ()
+pending :: WaiSession st ()
 pending = liftIO Core.pending
 
 -- | A lifted version of `Core.pendingWith`.
-pendingWith :: String -> WaiSession ()
+pendingWith :: String -> WaiSession st ()
 pendingWith = liftIO . Core.pendingWith
 
 -- | Set the expectation that a response matches a specified `ResponseMatcher`.
@@ -100,39 +112,39 @@
 --
 -- > get "/" `shouldRespondWith` "foo" {matchHeaders = ["Content-Type" <:> "text/plain"]}
 -- > -- matches if body is "foo", status is 200 and there is a header field "Content-Type: text/plain"
-shouldRespondWith :: HasCallStack => WaiSession SResponse -> ResponseMatcher -> WaiExpectation
+shouldRespondWith :: HasCallStack => WaiSession st SResponse -> ResponseMatcher -> WaiExpectation st
 shouldRespondWith action matcher = do
   r <- action
   forM_ (match r matcher) (liftIO . expectationFailure)
 
 -- | Perform a @GET@ request to the application under test.
-get :: ByteString -> WaiSession SResponse
+get :: ByteString -> WaiSession st SResponse
 get path = request methodGet path [] ""
 
 -- | Perform a @POST@ request to the application under test.
-post :: ByteString -> LB.ByteString -> WaiSession SResponse
+post :: ByteString -> LB.ByteString -> WaiSession st SResponse
 post path = request methodPost path []
 
 -- | Perform a @PUT@ request to the application under test.
-put :: ByteString -> LB.ByteString -> WaiSession SResponse
+put :: ByteString -> LB.ByteString -> WaiSession st SResponse
 put path = request methodPut path []
 
 -- | Perform a @PATCH@ request to the application under test.
-patch :: ByteString -> LB.ByteString -> WaiSession SResponse
+patch :: ByteString -> LB.ByteString -> WaiSession st SResponse
 patch path = request methodPatch path []
 
 -- | Perform an @OPTIONS@ request to the application under test.
-options :: ByteString -> WaiSession SResponse
+options :: ByteString -> WaiSession st SResponse
 options path = request methodOptions path [] ""
 
 -- | Perform a @DELETE@ request to the application under test.
-delete :: ByteString -> WaiSession SResponse
+delete :: ByteString -> WaiSession st SResponse
 delete path = request methodDelete path [] ""
 
 -- | Perform a request to the application under test, with specified HTTP
 -- method, request path, headers and body.
-request :: Method -> ByteString -> [Header] -> LB.ByteString -> WaiSession SResponse
-request method path headers body = getApp >>= liftIO . runSession (Wai.srequest $ SRequest req body)
+request :: Method -> ByteString -> [Header] -> LB.ByteString -> WaiSession st SResponse
+request method path headers = WaiSession . lift . Wai.srequest . SRequest req
   where
     req = setPath defaultRequest {requestMethod = method, requestHeaders = headers} path
 
@@ -142,5 +154,5 @@
 -- @application/x-www-form-urlencoded@ and used as request body.
 --
 -- In addition the @Content-Type@ is set to @application/x-www-form-urlencoded@.
-postHtmlForm :: ByteString -> [(String, String)] -> WaiSession SResponse
+postHtmlForm :: ByteString -> [(String, String)] -> WaiSession st SResponse
 postHtmlForm path = request methodPost path [(hContentType, "application/x-www-form-urlencoded")] . formUrlEncodeQuery
diff --git a/src/Test/Hspec/Wai/Internal.hs b/src/Test/Hspec/Wai/Internal.hs
--- a/src/Test/Hspec/Wai/Internal.hs
+++ b/src/Test/Hspec/Wai/Internal.hs
@@ -7,48 +7,54 @@
   WaiExpectation
 , WaiSession(..)
 , runWaiSession
+, runWithState
 , withApplication
 , getApp
+, getState
 , formatHeader
 ) where
 
-import           Prelude ()
-import           Prelude.Compat
-
 import           Control.Monad.IO.Class
+import           Control.Monad.Trans.Class
 import           Control.Monad.Trans.Reader
 import           Network.Wai (Application)
 import           Network.Wai.Test hiding (request)
 import           Test.Hspec.Core.Spec
 import           Test.Hspec.Wai.Util (formatHeader)
 
-#if MIN_VERSION_base(4,9,0)
+#if MIN_VERSION_base(4,9,0) && !MIN_VERSION_base(4,13,0)
 import           Control.Monad.Fail
 #endif
 
 -- | An expectation in the `WaiSession` monad.  Failing expectations are
 -- communicated through exceptions (similar to `Test.Hspec.Expectations.Expectation` and
 -- `Test.HUnit.Base.Assertion`).
-type WaiExpectation = WaiSession ()
+type WaiExpectation st = WaiSession st ()
 
 -- | A <http://www.yesodweb.com/book/web-application-interface WAI> test
 -- session that carries the `Application` under test and some client state.
-newtype WaiSession a = WaiSession {unWaiSession :: Session a}
+newtype WaiSession st a = WaiSession {unWaiSession :: ReaderT st Session a}
   deriving (Functor, Applicative, Monad, MonadIO
 #if MIN_VERSION_base(4,9,0)
   , MonadFail
 #endif
   )
 
-runWaiSession :: WaiSession a -> Application -> IO a
-runWaiSession = runSession . unWaiSession
+runWaiSession :: WaiSession () a -> Application -> IO a
+runWaiSession action app = runWithState action ((), app)
 
-withApplication :: Application -> WaiSession a -> IO a
+runWithState :: WaiSession st a -> (st, Application) -> IO a
+runWithState action (st, app) = runSession (flip runReaderT st $ unWaiSession action) app
+
+withApplication :: Application -> WaiSession () a -> IO a
 withApplication = flip runWaiSession
 
-instance Example WaiExpectation where
-  type Arg WaiExpectation = Application
-  evaluateExample e p action = evaluateExample (action $ runWaiSession e) p ($ ())
+instance Example (WaiExpectation st) where
+  type Arg (WaiExpectation st) = (st, Application)
+  evaluateExample e p action = evaluateExample (action $ runWithState e) p ($ ())
 
-getApp :: WaiSession Application
-getApp = WaiSession ask
+getApp :: WaiSession st Application
+getApp = WaiSession (lift ask)
+
+getState :: WaiSession st st
+getState = WaiSession ask
diff --git a/src/Test/Hspec/Wai/Matcher.hs b/src/Test/Hspec/Wai/Matcher.hs
--- a/src/Test/Hspec/Wai/Matcher.hs
+++ b/src/Test/Hspec/Wai/Matcher.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE PackageImports #-}
 {-# LANGUAGE ViewPatterns #-}
 module Test.Hspec.Wai.Matcher (
   ResponseMatcher(..)
@@ -11,7 +12,7 @@
 ) where
 
 import           Prelude ()
-import           Prelude.Compat
+import "base-compat" Prelude.Compat
 
 import           Control.Monad
 import           Data.Maybe
diff --git a/src/Test/Hspec/Wai/QuickCheck.hs b/src/Test/Hspec/Wai/QuickCheck.hs
--- a/src/Test/Hspec/Wai/QuickCheck.hs
+++ b/src/Test/Hspec/Wai/QuickCheck.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE TypeFamilies #-}
 module Test.Hspec.Wai.QuickCheck (
   property
 , (==>)
@@ -19,26 +20,30 @@
 
 import           Test.Hspec.Wai.Internal
 
-property :: Testable a => a -> Application -> Property
+property :: Testable a => a -> (State a, Application) -> Property
 property = unWaiProperty . toProperty
 
-data WaiProperty = WaiProperty {unWaiProperty :: Application -> Property}
+data WaiProperty st = WaiProperty {unWaiProperty :: (st, Application) -> Property}
 
 class Testable a where
-  toProperty :: a -> WaiProperty
+  type State a
+  toProperty :: a -> WaiProperty (State a)
 
-instance Testable WaiProperty where
+instance Testable (WaiProperty st) where
+  type State (WaiProperty st) = st
   toProperty = id
 
-instance Testable WaiExpectation where
-  toProperty action = WaiProperty (QuickCheck.property . runWaiSession action)
+instance Testable (WaiExpectation st) where
+  type State (WaiExpectation st) = st
+  toProperty action = WaiProperty (QuickCheck.property . runWithState action)
 
 instance (Arbitrary a, Show a, Testable prop) => Testable (a -> prop) where
+  type State (a -> prop) = State prop
   toProperty prop = WaiProperty $ QuickCheck.property . (flip $ unWaiProperty . toProperty . prop)
 
 infixr 0 ==>
-(==>) :: Testable prop => Bool -> prop -> WaiProperty
+(==>) :: Testable prop => Bool -> prop -> WaiProperty (State prop)
 (==>) = lift (QuickCheck.==>)
 
-lift :: Testable prop => (a -> Property -> Property) -> a -> prop -> WaiProperty
+lift :: Testable prop => (a -> Property -> Property) -> a -> prop -> WaiProperty (State prop)
 lift f a prop = WaiProperty $ \app -> f a (unWaiProperty (toProperty prop) app)
diff --git a/src/Test/Hspec/Wai/Util.hs b/src/Test/Hspec/Wai/Util.hs
--- a/src/Test/Hspec/Wai/Util.hs
+++ b/src/Test/Hspec/Wai/Util.hs
@@ -12,8 +12,8 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Char8 as B8
 import qualified Data.ByteString.Lazy as LB
-import           Data.ByteString.Lazy.Builder (Builder)
-import qualified Data.ByteString.Lazy.Builder as Builder
+import           Data.ByteString.Builder (Builder)
+import qualified Data.ByteString.Builder as Builder
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as T
 import qualified Data.CaseInsensitive as CI
diff --git a/test/Test/Hspec/WaiSpec.hs b/test/Test/Hspec/WaiSpec.hs
--- a/test/Test/Hspec/WaiSpec.hs
+++ b/test/Test/Hspec/WaiSpec.hs
@@ -23,7 +23,7 @@
   requestMethod req `shouldBe` method
   rawPathInfo req `shouldBe` path
   requestHeaders req `shouldBe` headers
-  rawBody <- requestBody req
+  rawBody <- getRequestBodyChunk req
   rawBody `shouldBe` body
   respond $ responseLBS status200 [] ""
 
