HSpec 中的多个前置函数?
Multiple before functions in HSpec?
我有一个可以通过调用此函数创建的内存存储库:
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
不在范围内。但我怎样才能做到这一点? IE。我想为 app
和测试共享 newEmptyRepository
的单个实例?
Ps:您可以在 github.
上看到完整的申请
您应该使用类型为
的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
- 这个 运行s startGame
每次构建测试树时(尽管,这实际上可能是你想要什么,它似乎并非如此)。相反,如果您使用 before
函数族,startGame
将在测试实际 运行 之前 运行 一次。您甚至可以使用与上述相同的技术访问 startGame
返回的 Game
:
before newEmptyRepository
. beforeWith (withArg startGame)
. beforeWith (withArg $ app . fst)
:: SpecWith ((InMemoryGameRepository, Game), Application) -> Spec
我有一个可以通过调用此函数创建的内存存储库:
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
不在范围内。但我怎样才能做到这一点? IE。我想为 app
和测试共享 newEmptyRepository
的单个实例?
Ps:您可以在 github.
上看到完整的申请您应该使用类型为
的beforeWithbeforeWith :: (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
- 这个 运行s startGame
每次构建测试树时(尽管,这实际上可能是你想要什么,它似乎并非如此)。相反,如果您使用 before
函数族,startGame
将在测试实际 运行 之前 运行 一次。您甚至可以使用与上述相同的技术访问 startGame
返回的 Game
:
before newEmptyRepository
. beforeWith (withArg startGame)
. beforeWith (withArg $ app . fst)
:: SpecWith ((InMemoryGameRepository, Game), Application) -> Spec