【问题标题】:How do I modify my Yesod app during a test?如何在测试期间修改我的 Yesod 应用程序?
【发布时间】:2016-09-07 04:11:38
【问题描述】:

我有一个 Yesod 应用,其类型为:

data App = App
    { appSettings    :: AppSettings
    , appStatic      :: Static
    , appConnPool    :: ConnectionPool
    , appHttpManager :: Manager
    , appLogger      :: Logger
    , appStripe      :: forall a. ((FromJSON (StripeReturn a)), Typeable (StripeReturn a))
                     => StripeConfig
                     -> StripeRequest a
                     -> IO (Either StripeError (StripeReturn a))
    }

还有一个辅助函数

stripe :: (FromJSON (StripeReturn a), Typeable (StripeReturn a))
       => StripeRequest a
       -> Handler (Either StripeError (StripeReturn a))
stripe req = do
  f <- appStripe <$> getYesod
  c <- appStripeConfig . appSettings <$> getYesod
  liftIO $ f c req

在多个处理程序中使用。 (AppappStripe 字段从未在任何处理程序中直接引用。)在makeFoundation 中,所有内容都作为脚手架,除了appStripe 字段用来自stripe-haskell 库的Web.Stripe.stripe 填充.

在我的测试中,我希望能够模拟对 Stripe 的调用,所以我有以下功能:

withStripeExpecting :: (FromJSON (StripeReturn a), Typeable (StripeReturn a))
                    => StripeRequest a
                    -> Either StripeError (StripeReturn a)
                    -> YesodExample App ()
                    -> YesodExample App ()
withStripeExpecting _expectedReq res = withStateT $ \yed -> yed {yedSite = f (yedSite yed)}
  where f app = app {appStripe = mock}
        mock :: Typeable (StripeReturn b)
             => StripeConfig
             -> StripeRequest b
             -> IO (Either StripeError (StripeReturn b))
        mock _ _actualReq = do
          -- assert actualReq matches expectedReq (in IO???)
          return $ case cast res of
                    Just a -> a
                    Nothing -> error "Stripe return types don’t match in mock."

我在测试用例中使用:

spec :: Spec
spec = withApp $ do
  describe "create" $ do
    it "returns a 201" $ do
      -- a bunch of set-up elided
      withStripeExpecting stripeReq (Right stripeRes) $ do
        requestWithSubject "auth0|fake" $ do
          setMethod "POST"
          setUrl $ SubscriptionPlansR walletId
          setRequestBody encoded
          addRequestHeader (H.hContentType, "application/json")
        statusIs 201

编译并运行,但抛出错误StripeError {errorType = InvalidRequest, errorMsg = "Invalid API Key provided: ", errorCode = Nothing, errorParam = Nothing, errorHTTP = Just UnAuthorized},表明它正在运行真正的条带 IO 操作而不是模拟。

如何在测试期间更改App 的字段,以便被测试的处理程序使用它?

【问题讨论】:

  • 你的 withStripeExpecting 块或 withApp 初始化是否发生错误?我建议修改 withApp 函数(它是脚手架的)

标签: haskell testing yesod


【解决方案1】:

我在 Yesod 的 Google Group 上发布了对这个问题的引用,并收到了 Yesod 创始人 Michael Snoyman 的this response

IIUC,您只需在代码中的不同位置覆盖该字段。以脚手架站点为例,我会在这里覆盖:

https://github.com/yesodweb/yesod-scaffold/blob/68ec6af22248de6c2a00e63624c34350846557cf/test/TestImport.hs#L35

根据该建议,需要对 Yesod.Test 模块进行一些修改,以将模拟线程控制通过线程控制到测试用例。这些更改已在Pull Request #1274 中捕获到 GitHub 上的 yesod 存储库。

有了 yesod-test 的修改版本,我能够用以下三行替换 Michael Snoyman 指出的行:

mocks <- newEmptyMVar
let foundation' = foundation { appStripe = mockStripe mocks }
return (foundation', logWare, mocks)

我还在TestImport 模块中添加了以下支持定义:

data StripeMock = forall a. Typeable (StripeReturn a)
               => StripeMock
                { stripeReq :: StripeRequest a
                , stripeRet :: Either StripeError (StripeReturn a)
                }

type Mocks = MVar StripeMock

type Yex = YesodExample App Mocks

mockStripe :: (Typeable (StripeReturn b))
           => Mocks
           -> StripeConfig
           -> StripeRequest b
           -> IO (Either StripeError (StripeReturn b))
mockStripe mocks _ _actualReq = do
  (StripeMock _expectedReq res) <- takeMVar mocks
  -- assert actualReq matches expectedReq (in IO???)
  return $ case cast res of
            Just a -> a
            Nothing -> error "Stripe return types don’t match in mock."

stripeExpects :: (FromJSON (StripeReturn a), Typeable (StripeReturn a))
              => StripeRequest a
              -> Either StripeError (StripeReturn a)
              -> Yex ()
stripeExpects expectedReq res = do
  mocks <- getMocks
  putMVar mocks $ StripeMock expectedReq res

stripeExpects 辅助函数替换了withStripeExpecting,并且不像withStripeExpecting 那样包装请求。

如拉取请求中所示,我正在尝试将此容量包含在 yesod-test 包中。如果我对此有所了解,我会更新这个答案。

【讨论】:

    猜你喜欢
    • 2015-06-30
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 2011-05-08
    • 2011-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多