这是我的(简化)测试
it "asserts route access for valid arguments" $ do
-- ...
token <- getFlagTokenFromCsrf "1234"
get $ FlagMentorR flaggedId Flag token
statusIs 200
我有getFlagTokenFromCsrf :: Text -> Handler Text
我尝试stack test
时遇到的错误是:
Couldn't match type ‘Yesod.Core.Types.HandlerT App IO Text’
with ‘Control.Monad.Trans.State.Lazy.StateT
(YesodExampleData App) IO Text’
Expected type: Control.Monad.Trans.State.Lazy.StateT
(YesodExampleData App) IO Text
Actual type: Handler Text
(这是full example)
答案 0 :(得分:2)
我似乎正在尝试做一些相似的事情。即:尝试在单元测试中运行任意Handler a
函数。
我尝试过的一些方法:
我最接近的确实对我有用:
import Application (handler)
spec = withApp $ do
it "" $ do
liftIO $ handler $ someHandler
虽然it seems this calls makeFoundation
under the hood,但我更喜欢我的处理程序在主Web应用程序的上下文中运行而不是单独的实例。
要在主应用程序的上下文中执行处理程序,我还尝试过:
import Foundation (unsafeHandler)
import qualified Control.Monad.Trans.State as ST
spec = withApp $ do
it "" $ do
(YesodExampleData app _ _ _) <- ST.get
liftIO $ unsafeHandler app $ someHandler
这让我遇到了类似的错误:
[34 of 35] Compiling Handler.FooSpec ( /path/to/test/Handler/FooSpec.hs, /path/to/.stack-work/odir/Handler/FooSpec.o )
/path/to/test/Handler/FooSpec.hs:42:31:
Couldn't match type ‘Network.Wai.Internal.Request
-> (Network.Wai.Internal.Response
-> IO Network.Wai.Internal.ResponseReceived)
-> IO Network.Wai.Internal.ResponseReceived’
with ‘App’
Expected type: App
Actual type: Network.Wai.Application
Probable cause: ‘app’ is applied to too few arguments
In the first argument of ‘unsafeHandler’, namely ‘app’
In the expression: unsafeHandler app
Failed, modules loaded: Import, Utils, Foundation, [...]
我并不完全清楚Network.Wai.Application
和App
之间可能存在什么区别,但这可能是实现这项工作的关键。
我还考虑过指定处理程序的路由,然后可以通过get SomeRouteR
访问它。虽然如果我能提供帮助,我宁愿不必这样做。
Handler a
函数重构为IO a
,然后我可以使用liftIO
来执行它们,不仅仅是处理程序,还包括测试。< / LI>
醇>
就我而言。如果我想出更多的东西,我打算更新这个答案。我也主演了这个问题,所以如果其他人找到更多或更好的东西,我会收到通知。
修改:I've also asked this question on GitHub。
编辑:
在尝试进一步向选项#2的方向挖掘时,我发现了一些相当突发的东西:
import Foundation (unsafeHandler)
runHandler :: Handler a -> ST.StateT (YesodExampleData App) IO a
runHandler handler = do
foundation <- getTestYesod
liftIO $ unsafeHandler foundation handler
我可以像这样运行测试:
it "runHandler" $ do
let
testHandler :: Handler Int
testHandler = do
return 2
runHandler testHandler >>= (==? 2)
let
testHandler2 :: Handler String
testHandler2 = do
fmap show $ (Import.runDB) $ insert (def :: User)
runHandler testHandler2 >>= (==? ("UserKey {unUserKey = SqlBackendKey {unSqlBackendKey = 1}}"))
答案 1 :(得分:1)
我不是Yesod专家,但你想要做的事情可能没有意义:getFlagTokenFromCsrf
是一个服务器端函数,它从请求中提取一些信息,因此它存在于Handler a
monad,因为这是服务器端代码的monad。
相比之下,Yesod测试旨在进行集成测试,从客户端对您的Application
断言行为(请参阅https://hackage.haskell.org/package/yesod-test-1.5.3/docs/Yesod-Test.html)。那里的功能住在不同的monad中,即YesodExample site a
代表你管理请求。此monad允许您重用部分服务器代码(路由,数据类型)并管理数据库的状态,但与服务器端操作完全不相关。
我不确定你要做什么,但如果你需要在服务器端生成一些信息,你必须找到另一种方法让它在客户端上可用。