首页 > 解决方案 > 使用来自 Tasty 的 Warp 的 `testWithApplication`

问题描述

Tasty只有withResource功能来管理测试中的资源。

该函数将资源初始化和清理函数作为参数:

withResource :: IO a -> (a -> IO ()) -> TestTree -> TestTree

我正在尝试测试一个仆人应用程序,所以我想使用Warp.testWithApplication每个仆人的测试教程,但这是一个with*风格的函数(一个调用你的动作并为你管理资源的函数),即类型是:

testWithApplication :: IO Application -> (Port -> IO a) -> IO a

考虑到我没有初始化程序/清理函数而是包装with...函数,我是否遗漏了一些东西,或者真的很难将两者粘合在一起?

笔记

我知道我可以用testWithApplication(in main) 包装整个测试套件,但如果 API 仅针对需要它的 TestTree 启动,我更愿意。

标签: haskellservanttasty

解决方案


我知道我可以用 testWithApplication (在 main 中)包装整个测试套件,但如果 API 仅针对需要它的 TestTree 启动,我更愿意。

是的,所以有两种不同的方法可以实现这一点。

一种方法是链接教程对 Hspec 的作用around:为每个单独的测试启动和停止您的应用程序。

Tasty 没有为此用例提供单独的实用程序,因为自己做这件事相当简单:只需testWithApplication在每个内部调用testCase(或者您可以为组合定义一个别名testCasetestWithApplication节省一些输入)。但是,如果您对同一个应用程序有多个测试,您可能会认为为每个单独的测试启动和停止它有点浪费。

这就是美味withResource(或 HSpec aroundAll)的用武之地。当您使用它时,应用程序会在需要它的第一个测试之前启动,并在最后一次此类测试之后立即停止。

但这主要依赖于资源的动态范围——即我们事先不知道资源将按什么顺序分配或取消分配。(如果您将执行测试套件视为遍历测试树并按顺序运行所有测试,这可能看起来令人惊讶。但由于并行测试执行和测试之间的依赖关系,实际情况更加复杂,因此实际执行顺序可以是任意的。 )

并且该动态范围与“with”风格的函数不兼容,后者通常只创建嵌套的、类似堆栈的范围。

所以你的选择是:

  1. 在顶层启动一个应用程序并让它在整个测试套件期间运行(您在问题中描述的内容)。
  2. 在底层(即在每个testCase)启动和停止应用程序,这是链接教程对 Hspec 所做的。
  3. 不要使用testWithApplication,而是编写单独的函数来启动和停止应用程序。testWithApplication与单独的获取/释放函数相比,它的吸引力在于它为您处理异常并确保释放操作将运行,但美味已经处理好了。
  4. 最后,我在Decompose ContT中描述了一个技巧,它使用线程通过“with”风格的函数(相当于 ContT 值)实现动态范围。testWithApplication如果分解成单独的获取/释放操作太麻烦,这可能是最实用的解决方案。HSpec 的aroundAll似乎也在使用类似的和基于线程的东西。

推荐阅读