-- | Yesod.Test.Json provides convenience functions for working
-- with Test.Hspec and Network.Wai.Test on JSON data.
module Yesod.Test.Json (
testApp,
APIFunction,
assertBool,
assertString,
assertOK,
assertJSON,
Session(..),
H.Assertion,
module Test.Hspec,
module Data.Aeson,
SResponse(..)
) where
import qualified Test.HUnit as H
import qualified Data.ByteString.Lazy.Char8 as L8
import qualified Data.ByteString.Lazy as L
import Data.ByteString (ByteString)
import Data.Text (Text)
import Data.Aeson
import Network.HTTP.Types
import Test.Hspec
import Network.Wai
import Network.Wai.Test
import Control.Monad.IO.Class
import Yesod.Default.Config
import Data.Conduit.List
-- | A request to your server.
type APIFunction = ByteString -- ^ method
-> [Text] -- ^ path
-> Maybe Value -- JSON data
-> Session SResponse
-- Assert a boolean value
assertBool :: String -> Bool -> Session ()
assertBool s b = liftIO $ H.assertBool s b
-- Fail a test with an error string
assertString :: String -> Session ()
assertString = liftIO . H.assertString
-- Assert a 200 response code
assertOK :: SResponse -> Session ()
assertOK SResponse{simpleStatus = s, simpleBody = b} = assertBool (concat
[ "Expected status code 200, but received "
, show sc
, ". Response body: "
, show (L8.unpack b)
]) $ sc == 200
where
sc = statusCode s
-- Assert a JSON body meeting some requirement
assertJSON :: (ToJSON a, FromJSON a) => (a -> (String, Bool)) -> SResponse -> Session ()
assertJSON f SResponse{simpleBody = lbs} = do
case decode lbs of
Nothing -> assertString $ "Invalid JSON: " ++ show (L8.unpack lbs)
Just a ->
case fromJSON a of
Error s -> assertString (concat [s, "\nInput JSON: ", show a])
Success x -> uncurry assertBool (f x)
-- | Make a request to your server
apiRequest :: AppConfig env extra -> APIFunction
apiRequest conf m p x = srequest $ SRequest r (maybe L.empty encode x) where
r = defaultRequest {
serverPort = appPort conf,
requestBody = sourceList . L.toChunks $ encode x,
requestMethod = m,
pathInfo = p
}
-- | Run a test suite for your 'Application'
testApp :: Application -> AppConfig env extra ->
(((APIFunction -> Session ()) -> H.Assertion) -> Spec) -> IO ()
testApp app conf specfun = do
let apiTest f = runSession (f (apiRequest conf)) app
hspec $ (specfun apiTest)