代码之家  ›  专栏  ›  技术社区  ›  Johan

HSpec中的多个before函数?

  •  1
  • Johan  · 技术社区  · 7 年前

    我有一个内存存储库,可以通过调用以下函数来创建:

    newEmptyRepository :: IO InMemoryGameRepository
    

    哪里 InMemoryGameRepository 定义如下:

    type State = (HashMap GameId Game)
    type IORefState = IORef State
    newtype InMemoryGameRepository = InMemoryGameRepository IORefState
    

    在为Scotty应用程序编写测试时,我看到了使用这种方法的示例:

    spec =
      before app $ do
        describe "GET /" $ do
          it "responds with 200" $ get "/" `shouldRespondWith` 200
          it "responds with 'hello'" $ get "/" `shouldRespondWith` "hello"
        ...
    

    这很好,但我还需要以某种方式初始化InMemoryGameRepository(通过调用 newEmptyRepository )并在测试中使用创建的实例。所以我改变了 app 收件人:

    app :: InMemoryGameRepository -> IO Application
    app repo = scottyApp $ routes repo
    

    我正在尝试创建一个使用存储库和 IO Application ,例如这样(这不起作用):

    spec = 
        before (do repo <- newEmptyRepository
                   app repo) $ 
          -- API Tests
          describe "GET /api/games" $ 
            it "responds with " $ do
              liftIO $ startGame repo
              get "/api/games" `shouldRespondWith` singleGameResponse
    

    哪里 startGame 定义如下:

    startGame :: InMemoryGameRepository -> IO Game
    

    这里编译器(显然)说 repo 不在范围内。但我如何才能做到这一点?一、 e.我想共享的单个实例 newEmptyRepository新仓库 对于 应用程序 在测试中呢?

    Ps:您可以在上看到完整的应用程序 github .

    1 回复  |  直到 7 年前
        1
  •  2
  •   user2407038    7 年前

    您应该使用 beforeWith 类型为

    beforeWith :: (b -> IO a) -> SpecWith a -> SpecWith b
    

    将其用作。 before newEmptyRepository . beforeWith app 其类型为 SpecWith Application -> Spec .

    如果要访问 InMemoryGameRepository 以及 Application 在测试用例中,定义了一个助手函数

    withArg f a = (,) a <$> f a
    withArg :: Functor f => (t -> f b) -> t -> f (t, b)
    

    然后使用

    before newEmptyRepository . beforeWith (withArg app)
        :: SpecWith (InMemoryGameRepository, Application) -> Spec
    

    最后,你不应该使用 liftIO $ startGame repo 在测试的定义中-此 startGame 每次构建测试树时(虽然,这可能实际上是您想要的,但事实似乎并非如此)。相反,如果您使用 before 功能系列, 开始名称 将在实际运行测试之前运行一次。您甚至可以访问 Game 返回人 开始名称 使用与上述相同的技术:

      before newEmptyRepository 
    . beforeWith (withArg startGame) 
    . beforeWith (withArg $ app . fst)
    :: SpecWith ((InMemoryGameRepository, Game), Application) -> Spec