#haskell #yesod
#haskell #yesod
Вопрос:
Вот (упрощенный) тест, который у меня есть
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
(Вот полный пример)
Ответ №1:
Кажется, я пытаюсь сделать что-то довольно похожее. То есть: попробуйте запустить произвольные Handler a
функции в модульных тестах.
Некоторые подходы, которые я пробовал и обдумывал:
-
Самое близкое, что я получил, действительно сработало для меня:
import Application (handler) spec = withApp $ do it "" $ do liftIO $ handler $ someHandler
Хотя кажется, что это вызывает
makeFoundation
скрытый вызов, и я бы предпочел, чтобы мой обработчик выполнялся в контексте основного веб-приложения, а не отдельного экземпляра. -
Чтобы выполнить обработчик в контексте основного приложения, я также попытался:
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
их для выполнения не только в обработчиках, но и в тестах.
Это все, что я получил. Если я выясню что-то еще, я намерен обновить этот ответ. Я также задал этот вопрос, поэтому я получаю уведомление, если кто-то еще выяснит что-то большее или лучшее.
Редактировать: я также задавал этот вопрос на 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}}"))
Ответ №2:
Я не эксперт по Yesod, но то, что вы пытаетесь сделать, вероятно, не имеет смысла: getFlagTokenFromCsrf
это серверная функция, которая извлекает некоторую информацию из запроса, следовательно, она живет в Handler a
монаде, потому что это монада для серверного кода.
В отличие от этого, тесты Yesod предназначены для интеграционных тестов, утверждающих поведение на стороне клиента в отношении вашего Application
(см. https://hackage.haskell.org/package/yesod-test-1.5.3/docs/Yesod-Test.html ). Функции там живут в другой монаде, а именно YesodExample site a
, которая управляет запросами от вашего имени. Эта монада позволяет вам повторно использовать часть вашего серверного кода (маршруты, типы данных) и управлять состоянием вашей БД, но в остальном она совершенно не связана с операциями на стороне сервера.
Я не уверен, что вы пытаетесь сделать, но если вам нужна какая-то информация, сгенерированная на стороне сервера, вам нужно найти другой способ сделать ее доступной на стороне клиента.
Комментарии:
1. Я понимаю. Я думал, что можно было бы также использовать модульные тесты.
2. ДА. Я думаю, что модульные тесты в этом конкретном случае имели бы большой смысл. Или найдите способ ввести известный токен на стороне клиента, чтобы вы могли проверить из ответа сервера, действительно ли он проверил токен