Compare commits

...

1 Commits

Author SHA1 Message Date
Greg Weber
326d8edd58 make yesod-test work with mongo
depends on resource-pool branch of persistent
2012-08-04 17:32:08 -07:00
3 changed files with 31 additions and 24 deletions

View File

@ -1,5 +1,6 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-| {-|
Yesod.Test is a pragmatic framework for testing web applications built Yesod.Test is a pragmatic framework for testing web applications built
using wai and persistent. using wai and persistent.
@ -16,7 +17,7 @@ This is very useful for testing web applications built in yesod for example,
were your forms may have field names generated by the framework or a randomly were your forms may have field names generated by the framework or a randomly
generated '_nonce' field. generated '_nonce' field.
Your database is also directly available so you can use runDB to set up Your database is also directly available so you can use runDBRunner to set up
backend pre-conditions, or to assert that your session is having the desired effect. backend pre-conditions, or to assert that your session is having the desired effect.
-} -}
@ -47,7 +48,7 @@ module Yesod.Test (
addNonce, addNonce_, addNonce, addNonce_,
-- * Running database queries -- * Running database queries
runDB, runDBRunner,
-- * Assertions -- * Assertions
assertEqual, assertHeader, assertNoHeader, statusIs, bodyEquals, bodyContains, assertEqual, assertHeader, assertNoHeader, statusIs, bodyEquals, bodyContains,
@ -84,25 +85,26 @@ import qualified Control.Monad.Trans.State as ST
import Control.Monad.IO.Class import Control.Monad.IO.Class
import System.IO import System.IO
import Yesod.Test.TransversingCSS import Yesod.Test.TransversingCSS
import Database.Persist.GenericSql
import Data.Monoid (mappend) import Data.Monoid (mappend)
import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy as TL
import Data.Text.Lazy.Encoding (encodeUtf8, decodeUtf8) import Data.Text.Lazy.Encoding (encodeUtf8, decodeUtf8)
import Text.XML.Cursor hiding (element) import Text.XML.Cursor hiding (element)
import qualified Text.XML.Cursor as C import qualified Text.XML.Cursor as C
import qualified Text.HTML.DOM as HD import qualified Text.HTML.DOM as HD
import Data.Conduit.Pool (Pool)
import Control.Monad.Trans.Control (MonadBaseControl)
-- | The state used in 'describe' to build a list of specs -- | The state used in 'describe' to build a list of specs
data SpecsData = SpecsData Application ConnectionPool [Core.Spec] data SpecsData conn = SpecsData Application (Pool conn) [Core.Spec]
-- | The specs state monad is where 'describe' runs. -- | The specs state monad is where 'describe' runs.
type Specs = ST.StateT SpecsData IO () type Specs conn = ST.StateT (SpecsData conn) IO ()
-- | The state used in a single test case defined using 'it' -- | The state used in a single test case defined using 'it'
data OneSpecData = OneSpecData Application ConnectionPool CookieValue (Maybe SResponse) data OneSpecData conn = OneSpecData Application (Pool conn) CookieValue (Maybe SResponse)
-- | The OneSpec state monad is where 'it' runs. -- | The OneSpec state monad is where 'it' runs.
type OneSpec = ST.StateT OneSpecData IO type OneSpec conn = ST.StateT (OneSpecData conn) IO
data RequestBuilderData = RequestBuilderData [RequestPart] (Maybe SResponse) data RequestBuilderData = RequestBuilderData [RequestPart] (Maybe SResponse)
@ -121,7 +123,7 @@ type RequestBuilder = ST.StateT RequestBuilderData IO
-- the last received response. -- the last received response.
class HoldsResponse a where class HoldsResponse a where
readResponse :: a -> Maybe SResponse readResponse :: a -> Maybe SResponse
instance HoldsResponse OneSpecData where instance HoldsResponse (OneSpecData conn) where
readResponse (OneSpecData _ _ _ x) = x readResponse (OneSpecData _ _ _ x) = x
instance HoldsResponse RequestBuilderData where instance HoldsResponse RequestBuilderData where
readResponse (RequestBuilderData _ x) = x readResponse (RequestBuilderData _ x) = x
@ -136,21 +138,21 @@ type CookieValue = ByteString
-- --
-- Look at the examples directory on this package to get an idea of the (small) amount of -- Look at the examples directory on this package to get an idea of the (small) amount of
-- boilerplate code you'll need to write before calling this. -- boilerplate code you'll need to write before calling this.
runTests :: Application -> ConnectionPool -> Specs -> IO () runTests :: Application -> Pool conn -> Specs conn -> IO ()
runTests app connection specsDef = do runTests app connection specsDef = do
(SpecsData _ _ specs) <- ST.execStateT specsDef (SpecsData app connection []) (SpecsData _ _ specs) <- ST.execStateT specsDef (SpecsData app connection [])
Runner.hspec specs Runner.hspec specs
-- | Start describing a Tests suite keeping cookies and a reference to the tested 'Application' -- | Start describing a Tests suite keeping cookies and a reference to the tested 'Application'
-- and 'ConnectionPool' -- and 'ConnectionPool'
describe :: String -> Specs -> Specs describe :: String -> Specs conn -> Specs conn
describe label action = do describe label action = do
sData <- ST.get sData <- ST.get
SpecsData app conn specs <- liftIO $ ST.execStateT action sData SpecsData app conn specs <- liftIO $ ST.execStateT action sData
ST.put $ SpecsData app conn [Core.describe label specs] ST.put $ SpecsData app conn [Core.describe label specs]
-- | Describe a single test that keeps cookies, and a reference to the last response. -- | Describe a single test that keeps cookies, and a reference to the last response.
it :: String -> OneSpec () -> Specs it :: String -> OneSpec conn () -> Specs conn
it label action = do it label action = do
SpecsData app conn specs <- ST.get SpecsData app conn specs <- ST.get
let spec = Core.it label $ do let spec = Core.it label $ do
@ -177,7 +179,7 @@ htmlQuery query = withResponse $ \ res ->
Right matches -> return $ map (encodeUtf8 . TL.pack) matches Right matches -> return $ map (encodeUtf8 . TL.pack) matches
-- | Asserts that the two given values are equal. -- | Asserts that the two given values are equal.
assertEqual :: (Eq a) => String -> a -> a -> OneSpec () assertEqual :: (Eq a) => String -> a -> a -> OneSpec conn ()
assertEqual msg a b = liftIO $ HUnit.assertBool msg (a == b) assertEqual msg a b = liftIO $ HUnit.assertBool msg (a == b)
-- | Assert the last response status is as expected. -- | Assert the last response status is as expected.
@ -341,24 +343,24 @@ addNonce :: RequestBuilder ()
addNonce = addNonce_ "" addNonce = addNonce_ ""
-- | Perform a POST request to url, using params -- | Perform a POST request to url, using params
post :: BS8.ByteString -> RequestBuilder () -> OneSpec () post :: BS8.ByteString -> RequestBuilder () -> OneSpec conn ()
post url paramsBuild = do post url paramsBuild = do
doRequest "POST" url paramsBuild doRequest "POST" url paramsBuild
-- | Perform a POST request without params -- | Perform a POST request without params
post_ :: BS8.ByteString -> OneSpec () post_ :: BS8.ByteString -> OneSpec conn ()
post_ = flip post $ return () post_ = flip post $ return ()
-- | Perform a GET request to url, using params -- | Perform a GET request to url, using params
get :: BS8.ByteString -> RequestBuilder () -> OneSpec () get :: BS8.ByteString -> RequestBuilder () -> OneSpec conn ()
get url paramsBuild = doRequest "GET" url paramsBuild get url paramsBuild = doRequest "GET" url paramsBuild
-- | Perform a GET request without params -- | Perform a GET request without params
get_ :: BS8.ByteString -> OneSpec () get_ :: BS8.ByteString -> OneSpec conn ()
get_ = flip get $ return () get_ = flip get $ return ()
-- | General interface to performing requests, letting you specify the request method and extra headers. -- | General interface to performing requests, letting you specify the request method and extra headers.
doRequest :: H.Method -> BS8.ByteString -> RequestBuilder a -> OneSpec () doRequest :: H.Method -> BS8.ByteString -> RequestBuilder a -> OneSpec conn ()
doRequest method url paramsBuild = do doRequest method url paramsBuild = do
OneSpecData app conn cookie mRes <- ST.get OneSpecData app conn cookie mRes <- ST.get
RequestBuilderData parts _ <- liftIO $ ST.execStateT paramsBuild $ RequestBuilderData [] mRes RequestBuilderData parts _ <- liftIO $ ST.execStateT paramsBuild $ RequestBuilderData [] mRes
@ -413,10 +415,13 @@ doRequest method url paramsBuild = do
-- | Run a persistent db query. For asserting on the results of performed actions -- | Run a persistent db query. For asserting on the results of performed actions
-- or setting up pre-conditions. At the moment this part is still very raw. -- or setting up pre-conditions. At the moment this part is still very raw.
runDB :: SqlPersist IO a -> OneSpec a --
runDB query = do -- It is intended that you parametize the first argument of this function for your backend
-- runDB = runDBRunnder SqlPersist
runDBRunner :: (MonadBaseControl IO m, MonadIO m) => (poolrunner m a -> Pool conn -> IO a) -> poolrunner m a -> OneSpec conn a
runDBRunner poolRunner query = do
OneSpecData _ pool _ _ <- ST.get OneSpecData _ pool _ _ <- ST.get
liftIO $ runSqlPool query pool liftIO $ poolRunner query pool
-- Yes, just a shortcut -- Yes, just a shortcut
failure :: (MonadIO a) => T.Text -> a b failure :: (MonadIO a) => T.Text -> a b

View File

@ -33,6 +33,8 @@ library
, html-conduit >= 0.1 && < 0.2 , html-conduit >= 0.1 && < 0.2
, blaze-html >= 0.5 && < 0.6 , blaze-html >= 0.5 && < 0.6
, blaze-markup >= 0.5.1 && < 0.6 , blaze-markup >= 0.5.1 && < 0.6
, pool-conduit
, monad-control
exposed-modules: Yesod.Test exposed-modules: Yesod.Test
Yesod.Test.CssQuery Yesod.Test.CssQuery

View File

@ -2,9 +2,8 @@ Default: &defaults
user: ~project~ user: ~project~
password: ~project~ password: ~project~
host: localhost host: localhost
port: 27017
database: ~project~ database: ~project~
poolsize: 10 connections: 10
Development: Development:
<<: *defaults <<: *defaults
@ -15,10 +14,11 @@ Testing:
Staging: Staging:
database: ~project~_staging database: ~project~_staging
poolsize: 100 connections: 100
<<: *defaults <<: *defaults
Production: Production:
database: ~project~_production database: ~project~_production
poolsize: 100 connections: 100
host: localhost
<<: *defaults <<: *defaults