diff --git a/Yesod/Test.hs b/Yesod/Test.hs
--- a/Yesod/Test.hs
+++ b/Yesod/Test.hs
@@ -319,7 +319,7 @@
 bodyEquals :: String -> YesodExample site ()
 bodyEquals text = withResponse $ \ res ->
   liftIO $ HUnit.assertBool ("Expected body to equal " ++ text) $
-    (simpleBody res) == BSL8.pack text
+    (simpleBody res) == encodeUtf8 (TL.pack text)
 
 -- | Assert the last response has the given text. The check is performed using the response
 -- body in full text form.
@@ -329,7 +329,7 @@
     (simpleBody res) `contains` text
 
 contains :: BSL8.ByteString -> String -> Bool
-contains a b = DL.isInfixOf b (BSL8.unpack a)
+contains a b = DL.isInfixOf b (TL.unpack $ decodeUtf8 a)
 
 -- | Queries the html using a css selector, and all matched elements must contain
 -- the given string.
@@ -364,7 +364,7 @@
 -- | Outputs the last response body to stderr (So it doesn't get captured by HSpec)
 printBody :: YesodExample site ()
 printBody = withResponse $ \ SResponse { simpleBody = b } ->
-  liftIO $ hPutStrLn stderr $ BSL8.unpack b
+  liftIO $ BSL8.hPutStrLn stderr b
 
 -- | Performs a CSS query and print the matches to stderr.
 printMatches :: Query -> YesodExample site ()
@@ -539,7 +539,7 @@
       , rbdGets = []
       , rbdHeaders = []
       }
-    let path = T.cons '/' $ T.intercalate "/" rbdPath
+    let path = TE.decodeUtf8 $ Builder.toByteString $ H.encodePathSegments rbdPath
 
     -- expire cookies and filter them for the current path. TODO: support max age
     currentUtc <- liftIO getCurrentTime
@@ -644,7 +644,7 @@
       , remoteHost = Sock.SockAddrInet 1 2
       , requestHeaders = headers ++ extraHeaders
       , rawPathInfo = TE.encodeUtf8 urlPath
-      , pathInfo = DL.filter (/="") $ T.split (== '/') urlPath
+      , pathInfo = H.decodePathSegments $ TE.encodeUtf8 urlPath
       , rawQueryString = H.renderQuery False urlQuery
       , queryString = urlQuery
       }
diff --git a/test/main.hs b/test/main.hs
--- a/test/main.hs
+++ b/test/main.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 import Test.HUnit hiding (Test)
@@ -13,6 +14,7 @@
 import Data.Text (Text)
 import Data.Monoid ((<>))
 import Control.Applicative
+import Network.Wai (pathInfo)
 
 import Data.ByteString.Lazy.Char8 ()
 import qualified Data.Map as Map
@@ -106,6 +108,15 @@
                     addNonce
                 statusIs 200
                 bodyEquals "12345"
+        ydescribe "utf8 paths" $ do
+            yit "from path" $ do
+                get ("/dynamic1/שלום" :: Text)
+                statusIs 200
+                bodyEquals "שלום"
+            yit "from WAI" $ do
+                get ("/dynamic2/שלום" :: Text)
+                statusIs 200
+                bodyEquals "שלום"
 
 instance RenderMessage LiteApp FormMessage where
     renderMessage _ _ = defaultFormMessage
@@ -117,6 +128,10 @@
         case mfoo of
             Nothing -> return "Hello world!"
             Just foo -> return $ "foo=" <> foo
+    onStatic "dynamic1" $ withDynamic $ \d -> dispatchTo $ return (d :: Text)
+    onStatic "dynamic2" $ onStatic "שלום" $ dispatchTo $ do
+        req <- waiRequest
+        return $ pathInfo req !! 1
     onStatic "post" $ dispatchTo $ do
         mfoo <- lookupPostParam "foo"
         case mfoo of
diff --git a/yesod-test.cabal b/yesod-test.cabal
--- a/yesod-test.cabal
+++ b/yesod-test.cabal
@@ -1,5 +1,5 @@
 name:               yesod-test
-version:            1.2.1.2
+version:            1.2.1.3
 license:            MIT
 license-file:       LICENSE
 author:             Nubis <nubis@woobiz.com.ar>
@@ -59,6 +59,7 @@
                           , yesod-core
                           , yesod-form
                           , text
+                          , wai
 
 source-repository head
   type: git
