diff --git a/runtests.hs b/runtests.hs
deleted file mode 100644
--- a/runtests.hs
+++ /dev/null
@@ -1,426 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-import Test.Hspec
-import Test.Hspec.HUnit ()
-import Test.HUnit hiding (Test)
-
-import Network.Wai
-import Network.Wai.Test
-import Network.Wai.Parse
-import qualified Data.ByteString as S
-import qualified Data.ByteString.Char8 as S8
-import qualified Data.ByteString.Lazy.Char8 as L8
-import qualified Data.ByteString.Lazy as L
-import qualified Data.Text.Lazy as T
-import Control.Arrow
-
-import Network.Wai.Middleware.Jsonp
-import Network.Wai.Middleware.Gzip
-import Network.Wai.Middleware.Vhost
-import Network.Wai.Middleware.Autohead
-import Network.Wai.Middleware.MethodOverride
-import Network.Wai.Middleware.AcceptOverride
-import Network.Wai.Middleware.Debug (debugHandle)
-import Codec.Compression.GZip (decompress)
-
-import Data.Enumerator (run_, enumList, ($$), Iteratee)
-import Data.Enumerator.Binary (enumFile)
-import Control.Monad.IO.Class (liftIO)
-import Data.Maybe (fromMaybe)
-import Network.HTTP.Types (parseSimpleQuery, status200)
-
-main :: IO ()
-main = hspecX $ do
-  describe "Network.Wai.Parse"
-    [ it "parseQueryString" caseParseQueryString
-    , it "parseQueryString with question mark" caseParseQueryStringQM
-    , it "parseHttpAccept" caseParseHttpAccept
-    , it "parseRequestBody" caseParseRequestBody
-    {-
-    , it "findBound" caseFindBound
-    , it "sinkTillBound" caseSinkTillBound
-    , it "killCR" caseKillCR
-    , it "killCRLF" caseKillCRLF
-    , it "takeLine" caseTakeLine
-    -}
-    , it "jsonp" caseJsonp
-    , it "gzip" caseGzip
-    , it "gzip not for MSIE" caseGzipMSIE
-    , it "vhost" caseVhost
-    , it "autohead" caseAutohead
-    , it "method override" caseMethodOverride
-    , it "accept override" caseAcceptOverride
-    , it "dalvik multipart" caseDalvikMultipart
-    , it "debug request body" caseDebugRequestBody
-    ]
-
-caseParseQueryString :: Assertion
-caseParseQueryString = do
-    let go l r =
-            map (S8.pack *** S8.pack) l @=? parseSimpleQuery (S8.pack r)
-
-    go [] ""
-    go [("foo", "")] "foo"
-    go [("foo", "bar")] "foo=bar"
-    go [("foo", "bar"), ("baz", "bin")] "foo=bar&baz=bin"
-    go [("%Q", "")] "%Q"
-    go [("%1Q", "")] "%1Q"
-    go [("%1", "")] "%1"
-    go [("/", "")] "%2F"
-    go [("/", "")] "%2f"
-    go [("foo bar", "")] "foo+bar"
-
-caseParseQueryStringQM :: Assertion
-caseParseQueryStringQM = do
-    let go l r =
-            map (S8.pack *** S8.pack) l
-                @=? parseSimpleQuery (S8.pack $ '?' : r)
-
-    go [] ""
-    go [("foo", "")] "foo"
-    go [("foo", "bar")] "foo=bar"
-    go [("foo", "bar"), ("baz", "bin")] "foo=bar&baz=bin"
-    go [("%Q", "")] "%Q"
-    go [("%1Q", "")] "%1Q"
-    go [("%1", "")] "%1"
-    go [("/", "")] "%2F"
-    go [("/", "")] "%2f"
-    go [("foo bar", "")] "foo+bar"
-
-caseParseHttpAccept :: Assertion
-caseParseHttpAccept = do
-    let input = "text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"
-        expected = ["text/html", "text/x-c", "text/x-dvi", "text/plain"]
-    expected @=? parseHttpAccept input
-
-parseRequestBody' :: Sink ([S8.ByteString] -> [S8.ByteString]) L.ByteString
-                  -> SRequest
-                  -> Iteratee S.ByteString IO ([(S.ByteString, S.ByteString)], [(S.ByteString, FileInfo L.ByteString)])
-parseRequestBody' sink (SRequest req bod) =
-    enumList 1 (L.toChunks bod) $$ parseRequestBody sink req
-
-caseParseRequestBody :: Assertion
-caseParseRequestBody = run_ t where
-    content2 = S8.pack $
-        "--AaB03x\n" ++
-        "Content-Disposition: form-data; name=\"document\"; filename=\"b.txt\"\n" ++
-        "Content-Type: text/plain; charset=iso-8859-1\n\n" ++
-        "This is a file.\n" ++
-        "It has two lines.\n" ++
-        "--AaB03x\n" ++
-        "Content-Disposition: form-data; name=\"title\"\n" ++
-        "Content-Type: text/plain; charset=iso-8859-1\n\n" ++
-        "A File\n" ++
-        "--AaB03x\n" ++
-        "Content-Disposition: form-data; name=\"summary\"\n" ++
-        "Content-Type: text/plain; charset=iso-8859-1\n\n" ++
-        "This is my file\n" ++
-        "file test\n" ++
-        "--AaB03x--"
-    content3 = S8.pack "------WebKitFormBoundaryB1pWXPZ6lNr8RiLh\r\nContent-Disposition: form-data; name=\"yaml\"; filename=\"README\"\r\nContent-Type: application/octet-stream\r\n\r\nPhoto blog using Hack.\n\r\n------WebKitFormBoundaryB1pWXPZ6lNr8RiLh--\r\n"
-    t = do
-        let content1 = "foo=bar&baz=bin"
-        let ctype1 = "application/x-www-form-urlencoded"
-        result1 <- parseRequestBody' lbsSink $ toRequest ctype1 content1
-        liftIO $ assertEqual "parsing post x-www-form-urlencoded"
-                    (map (S8.pack *** S8.pack) [("foo", "bar"), ("baz", "bin")], [])
-                    result1
-
-        let ctype2 = "multipart/form-data; boundary=AaB03x"
-        result2 <- parseRequestBody' lbsSink $ toRequest ctype2 content2
-        let expectedsmap2 =
-              [ ("title", "A File")
-              , ("summary", "This is my file\nfile test")
-              ]
-        let textPlain = S8.pack $ "text/plain; charset=iso-8859-1"
-        let expectedfile2 =
-              [(S8.pack "document", FileInfo (S8.pack "b.txt") textPlain $ L8.pack
-                 "This is a file.\nIt has two lines.")]
-        let expected2 = (map (S8.pack *** S8.pack) expectedsmap2, expectedfile2)
-        liftIO $ assertEqual "parsing post multipart/form-data"
-                    expected2
-                    result2
-
-        let ctype3 = "multipart/form-data; boundary=----WebKitFormBoundaryB1pWXPZ6lNr8RiLh"
-        result3 <- parseRequestBody' lbsSink $ toRequest ctype3 content3
-        let expectedsmap3 = []
-        let expectedfile3 = [(S8.pack "yaml", FileInfo (S8.pack "README") (S8.pack "application/octet-stream") $
-                                L8.pack "Photo blog using Hack.\n")]
-        let expected3 = (expectedsmap3, expectedfile3)
-        liftIO $ assertEqual "parsing actual post multipart/form-data"
-                    expected3
-                    result3
-
-        result2' <- parseRequestBody' lbsSink $ toRequest' ctype2 content2
-        liftIO $ assertEqual "parsing post multipart/form-data 2"
-                    expected2
-                    result2'
-
-        result3' <- parseRequestBody' lbsSink $ toRequest' ctype3 content3
-        liftIO $ assertEqual "parsing actual post multipart/form-data 2"
-                    expected3
-                    result3'
-
-toRequest :: S8.ByteString -> S8.ByteString -> SRequest
-toRequest ctype content = SRequest defaultRequest
-    { requestHeaders = [("Content-Type", ctype)]
-    , requestMethod = "POST"
-    , rawPathInfo = ""
-    , rawQueryString = ""
-    , queryString = []
-    } (L.fromChunks [content])
-
-toRequest' :: S8.ByteString -> S8.ByteString -> SRequest
-toRequest' ctype content = SRequest defaultRequest
-    { requestHeaders = [("Content-Type", ctype)]
-    } (L.fromChunks $ map S.singleton $ S.unpack content)
-
-{-
-caseFindBound :: Assertion
-caseFindBound = do
-    findBound (S8.pack "def") (S8.pack "abcdefghi") @?=
-        FoundBound (S8.pack "abc") (S8.pack "ghi")
-    findBound (S8.pack "def") (S8.pack "ABC") @?= NoBound
-    findBound (S8.pack "def") (S8.pack "abcd") @?= PartialBound
-    findBound (S8.pack "def") (S8.pack "abcdE") @?= NoBound
-    findBound (S8.pack "def") (S8.pack "abcdEdef") @?=
-        FoundBound (S8.pack "abcdE") (S8.pack "")
-
-caseSinkTillBound :: Assertion
-caseSinkTillBound = do
-    let iter () _ = return ()
-    let src = S8.pack "this is some text"
-        bound1 = S8.pack "some"
-        bound2 = S8.pack "some!"
-    let enum = enumList 1 [src]
-    let helper _ _ = return ()
-    (_, res1) <- run_ $ enum $$ sinkTillBound bound1 helper ()
-    res1 @?= True
-    (_, res2) <- run_ $ enum $$ sinkTillBound bound2 helper ()
-    res2 @?= False
-
-caseKillCR :: Assertion
-caseKillCR = do
-    "foo" @=? killCR "foo"
-    "foo" @=? killCR "foo\r"
-    "foo\r\n" @=? killCR "foo\r\n"
-    "foo\r'" @=? killCR "foo\r'"
-
-caseKillCRLF :: Assertion
-caseKillCRLF = do
-    "foo" @=? killCRLF "foo"
-    "foo\r" @=? killCRLF "foo\r"
-    "foo" @=? killCRLF "foo\r\n"
-    "foo\r'" @=? killCRLF "foo\r'"
-    "foo" @=? killCRLF "foo\n"
-
-caseTakeLine :: Assertion
-caseTakeLine = do
-    helper "foo\nbar\nbaz" "foo"
-    helper "foo\r\nbar\nbaz" "foo"
-    helper "foo\nbar\r\nbaz" "foo"
-    helper "foo\rbar\r\nbaz" "foo\rbar"
-  where
-    helper haystack needle = do
-        x <- run_ $ enumList 1 [haystack] $$ takeLine
-        Just needle @=? x
--}
-
-jsonpApp :: Application
-jsonpApp = jsonp $ const $ return $ responseLBS
-    status200
-    [("Content-Type", "application/json")]
-    "{\"foo\":\"bar\"}"
-
-caseJsonp :: Assertion
-caseJsonp = flip runSession jsonpApp $ do
-    sres1 <- request defaultRequest
-                { queryString = [("callback", Just "test")]
-                , requestHeaders = [("Accept", "text/javascript")]
-                }
-    assertContentType "text/javascript" sres1
-    assertBody "test({\"foo\":\"bar\"})" sres1
-
-    sres2 <- request defaultRequest
-                { queryString = [("call_back", Just "test")]
-                , requestHeaders = [("Accept", "text/javascript")]
-                }
-    assertContentType "application/json" sres2
-    assertBody "{\"foo\":\"bar\"}" sres2
-
-    sres3 <- request defaultRequest
-                { queryString = [("callback", Just "test")]
-                , requestHeaders = [("Accept", "text/html")]
-                }
-    assertContentType "application/json" sres3
-    assertBody "{\"foo\":\"bar\"}" sres3
-
-gzipApp :: Application
-gzipApp = gzip True $ const $ return $ responseLBS status200
-    [("Content-Type", "text/plain")]
-    "test"
-
-caseGzip :: Assertion
-caseGzip = flip runSession gzipApp $ do
-    sres1 <- request defaultRequest
-                { requestHeaders = [("Accept-Encoding", "gzip")]
-                }
-    assertHeader "Content-Encoding" "gzip" sres1
-    liftIO $ decompress (simpleBody sres1) @?= "test"
-
-    sres2 <- request defaultRequest
-                { requestHeaders = []
-                }
-    assertNoHeader "Content-Encoding" sres2
-    assertBody "test" sres2
-
-caseGzipMSIE :: Assertion
-caseGzipMSIE = flip runSession gzipApp $ do
-    sres1 <- request defaultRequest
-                { requestHeaders =
-                    [ ("Accept-Encoding", "gzip")
-                    , ("User-Agent", "Mozilla/4.0 (Windows; MSIE 6.0; Windows NT 6.0)")
-                    ]
-                }
-    assertNoHeader "Content-Encoding" sres1
-    liftIO $ simpleBody sres1 @?= "test"
-
-vhostApp1, vhostApp2, vhostApp :: Application
-vhostApp1 = const $ return $ responseLBS status200 [] "app1"
-vhostApp2 = const $ return $ responseLBS status200 [] "app2"
-vhostApp = vhost
-    [ ((== "foo.com") . serverName, vhostApp1)
-    ]
-    vhostApp2
-
-caseVhost :: Assertion
-caseVhost = flip runSession vhostApp $ do
-    sres1 <- request defaultRequest
-                { serverName = "foo.com"
-                }
-    assertBody "app1" sres1
-
-    sres2 <- request defaultRequest
-                { serverName = "bar.com"
-                }
-    assertBody "app2" sres2
-
-autoheadApp :: Application
-autoheadApp = autohead $ const $ return $ responseLBS status200
-    [("Foo", "Bar")] "body"
-
-caseAutohead :: Assertion
-caseAutohead = flip runSession autoheadApp $ do
-    sres1 <- request defaultRequest
-                { requestMethod = "GET"
-                }
-    assertHeader "Foo" "Bar" sres1
-    assertBody "body" sres1
-
-    sres2 <- request defaultRequest
-                { requestMethod = "HEAD"
-                }
-    assertHeader "Foo" "Bar" sres2
-    assertBody "" sres2
-
-moApp :: Application
-moApp = methodOverride $ \req -> return $ responseLBS status200
-    [("Method", requestMethod req)] ""
-
-caseMethodOverride :: Assertion
-caseMethodOverride = flip runSession moApp $ do
-    sres1 <- request defaultRequest
-                { requestMethod = "GET"
-                , queryString = []
-                }
-    assertHeader "Method" "GET" sres1
-
-    sres2 <- request defaultRequest
-                { requestMethod = "POST"
-                , queryString = []
-                }
-    assertHeader "Method" "POST" sres2
-
-    sres3 <- request defaultRequest
-                { requestMethod = "POST"
-                , queryString = [("_method", Just "PUT")]
-                }
-    assertHeader "Method" "PUT" sres3
-
-aoApp :: Application
-aoApp = acceptOverride $ \req -> return $ responseLBS status200
-    [("Accept", fromMaybe "" $ lookup "Accept" $ requestHeaders req)] ""
-
-caseAcceptOverride :: Assertion
-caseAcceptOverride = flip runSession aoApp $ do
-    sres1 <- request defaultRequest
-                { queryString = []
-                , requestHeaders = [("Accept", "foo")]
-                }
-    assertHeader "Accept" "foo" sres1
-
-    sres2 <- request defaultRequest
-                { queryString = []
-                , requestHeaders = [("Accept", "bar")]
-                }
-    assertHeader "Accept" "bar" sres2
-
-    sres3 <- request defaultRequest
-                { queryString = [("_accept", Just "baz")]
-                , requestHeaders = [("Accept", "bar")]
-                }
-    assertHeader "Accept" "baz" sres3
-
-caseDalvikMultipart :: Assertion
-caseDalvikMultipart = do
-    let headers =
-            [ ("content-length", "12098")
-            , ("content-type", "multipart/form-data;boundary=*****")
-            , ("GATEWAY_INTERFACE", "CGI/1.1")
-            , ("PATH_INFO", "/")
-            , ("QUERY_STRING", "")
-            , ("REMOTE_ADDR", "192.168.1.115")
-            , ("REMOTE_HOST", "ganjizza")
-            , ("REQUEST_URI", "http://192.168.1.115:3000/")
-            , ("REQUEST_METHOD", "POST")
-            , ("HTTP_CONNECTION", "Keep-Alive")
-            , ("HTTP_COOKIE", "_SESSION=fgUGM5J/k6mGAAW+MMXIJZCJHobw/oEbb6T17KQN0p9yNqiXn/m/ACrsnRjiCEgqtG4fogMUDI+jikoFGcwmPjvuD5d+MDz32iXvDdDJsFdsFMfivuey2H+n6IF6yFGD")
-            , ("HTTP_USER_AGENT", "Dalvik/1.1.0 (Linux; U; Android 2.1-update1; sdk Build/ECLAIR)")
-            , ("HTTP_HOST", "192.168.1.115:3000")
-            , ("HTTP_ACCEPT", "*, */*")
-            , ("HTTP_VERSION", "HTTP/1.1")
-            , ("REQUEST_PATH", "/")
-            ]
-    let request' = defaultRequest
-            { requestHeaders = headers
-            }
-    (params, files) <- run_ $ enumFile "test/dalvik-request" $$ parseRequestBody lbsSink request'
-    lookup "scannedTime" params @?= Just "1.298590056748E9"
-    lookup "geoLong" params @?= Just "0"
-    lookup "geoLat" params @?= Just "0"
-    length files @?= 1
-
-caseDebugRequestBody :: Assertion
-caseDebugRequestBody = do
-    flip runSession (debugApp postOutput) $ do
-        let req = toRequest "application/x-www-form-urlencoded" "foo=bar&baz=bin"
-        res <- srequest req
-        assertStatus 200 res
-
-    let qs = "?foo=bar&baz=bin"
-    flip runSession (debugApp $ getOutput qs) $ do
-        assertStatus 200 =<< request defaultRequest
-                { requestMethod = "GET"
-                , queryString = map (\(k,v) -> (k, Just v)) params
-                , rawQueryString = qs
-                , requestHeaders = []
-                , rawPathInfo = "/location"
-                }
-  where
-    params = [("foo", "bar"), ("baz", "bin")]
-    postOutput = T.pack $ "POST \nAccept: \nPOST " ++ (show params)
-    getOutput _qs = T.pack $ "GET /location" ++ "\nAccept: \nGET " ++ (show params) -- \nAccept: \n" ++ (show params)
-
-    debugApp output = debugHandle (\t -> liftIO $ assertEqual "debug" output t) $ \_req -> do
-        return $ responseLBS status200 [ ] ""
-    {-debugApp = debug $ \req -> do-}
-        {-return $ responseLBS status200 [ ] ""-}
diff --git a/test/dalvik-request b/test/dalvik-request
deleted file mode 100644
Binary files a/test/dalvik-request and /dev/null differ
diff --git a/test/requests/dalvik-request b/test/requests/dalvik-request
new file mode 100644
Binary files /dev/null and b/test/requests/dalvik-request differ
diff --git a/tests.hs b/tests.hs
new file mode 100644
--- /dev/null
+++ b/tests.hs
@@ -0,0 +1,5 @@
+import Test.Hspec.Monadic
+import qualified WaiExtraTest
+
+main :: IO ()
+main = hspecX WaiExtraTest.specs
diff --git a/wai-extra.cabal b/wai-extra.cabal
--- a/wai-extra.cabal
+++ b/wai-extra.cabal
@@ -1,5 +1,5 @@
 Name:                wai-extra
-Version:             0.4.4
+Version:             0.4.5
 Synopsis:            Provides some basic WAI handlers and middleware.
 Description:         The goal here is to provide common features without many dependencies.
 License:             BSD3
@@ -12,30 +12,31 @@
 Cabal-Version:       >=1.8
 Stability:           Stable
 extra-source-files:
-  runtests.hs
-  test/dalvik-request
+  tests.hs
+  test/requests/dalvik-request
   test/json
   test/test.html
   test/sample.hs
 
 Library
-  Build-Depends:     base >= 3 && < 5,
-                     bytestring >= 0.9 && < 0.10,
-                     wai >= 0.4 && < 0.5,
-                     old-locale >= 1.0 && < 1.1,
-                     time >= 1.1.4 && < 1.4,
-                     network >= 2.2.1.5 && < 2.4,
-                     directory >= 1.0.1 && < 1.2,
-                     zlib-bindings >= 0.0 && < 0.1,
-                     blaze-builder-enumerator >= 0.2 && < 0.3,
-                     transformers >= 0.2 && < 0.3,
-                     enumerator >= 0.4.7 && < 0.5,
-                     blaze-builder >= 0.2.1.3 && < 0.4,
-                     http-types >= 0.6 && < 0.7,
-                     text >= 0.5 && < 1.0,
-                     case-insensitive >= 0.2 && < 0.4,
-                     zlib-enum >= 0.2.1 && < 0.3,
-                     data-default >= 0.3 && < 0.4
+  Build-Depends:     base                      >= 4 && < 5
+                   , bytestring                >= 0.9.1.4  && < 0.10
+                   , wai                       >= 0.4      && < 0.5
+                   , old-locale                >= 1.0.0.2  && < 1.1
+                   , time                      >= 1.1.4    && < 1.4
+                   , network                   >= 2.2.1.5  && < 2.4
+                   , directory                 >= 1.0.1    && < 1.2
+                   , zlib-bindings             >= 0.0      && < 0.1
+                   , blaze-builder-enumerator  >= 0.2      && < 0.3
+                   , transformers              >= 0.2.2    && < 0.3
+                   , enumerator                >= 0.4.8    && < 0.5
+                   , blaze-builder             >= 0.2.1.4  && < 0.4
+                   , http-types                >= 0.6      && < 0.7
+                   , text                      >= 0.7      && < 0.12
+                   , case-insensitive          >= 0.2      && < 0.4
+                   , zlib-enum                 >= 0.2.1    && < 0.3
+                   , data-default              >= 0.3      && < 0.4
+
   Exposed-modules:   Network.Wai.Handler.CGI
                      Network.Wai.Middleware.AcceptOverride
                      Network.Wai.Middleware.Autohead
@@ -51,15 +52,15 @@
   ghc-options:       -Wall
 
 
-test-suite runtests
-    hs-source-dirs: .
-    main-is: runtests.hs
+test-suite tests
+    hs-source-dirs: test
+    main-is: ../tests.hs
     type: exitcode-stdio-1.0
 
     build-depends:   base                      >= 4        && < 5
                    , wai-extra
                    , wai-test
-                   , hspec >= 0.8 && < 0.9
+                   , hspec >= 0.8 && < 0.10
                    , HUnit
 
                    , wai
@@ -71,8 +72,8 @@
                    , bytestring
                    , directory 
                    , zlib-bindings
-                   , blaze-builder-enumerator
-                   , blaze-builder
+                   , blaze-builder-enumerator >= 0.2 && < 0.3
+                   , blaze-builder             >= 0.2.1.4  && < 0.4
                    , zlib-enum
                    , data-default
 
