使用ZIO测试套件对HTTP服务器进行集成测试

如何解决使用ZIO测试套件对HTTP服务器进行集成测试

我试图找出为支持两个端点的Http4s应用程序编写集成测试的习惯用法。 我正在通过在新光纤上分叉Main中的ZManaged应用程序类,然后在发布ZManaged时执行interruptFork。 然后,我将其转换为ZLayer并通过provideCustomLayerShared()在具有多个suite的整个testM上传递。

  1. 我在这里吗?
  2. 它不符合我的预期:
  • 尽管以上述方式管理的httpserver已提供给包含这两个测试的套件,但它会在第一个测试后释放,因此第二个测试将失败
  • 测试套件永远不会结束,只是在执行两个测试后才会挂起

以下代码的半熟性质致歉。

object MainTest extends DefaultRunnableSpec {

  def httpServer =
    ZManaged
      .make(Main.run(List()).fork)(fiber => {
        //fiber.join or Fiber.interrupt will not work here,hangs the test
        fiber.interruptFork.map(
          ex => println(s"stopped with exitCode: $ex")
        )
      })
      .toLayer

  val clockDuration = 1.second

  //did the httpserver start listening on 8080?
  private def isLocalPortInUse(port: Int): ZIO[Clock,Throwable,Unit] = {
    IO.effect(new Socket("0.0.0.0",port).close()).retry(Schedule.exponential(clockDuration) && Schedule.recurs(10))
  }

  override def spec: ZSpec[Environment,Failure] =
    suite("MainTest")(
      testM("Health check") {
        for {
          _ <- TestClock.adjust(clockDuration).fork
          _ <- isLocalPortInUse(8080)
          client <- Task(JavaNetClientBuilder[Task](blocker).create)
          response <- client.expect[HealthReplyDTO]("http://localhost:8080/health")
          expected = HealthReplyDTO("OK")
        } yield assert(response) {
          equalTo(expected)
        }
      },testM("Distances endpoint check") {
        for {
          _ <- TestClock.adjust(clockDuration).fork
          _ <- isLocalPortInUse(8080)
          client <- Task(JavaNetClientBuilder[Task](blocker).create)
          response <- client.expect[DistanceReplyDTO](
            Request[Task](method = Method.GET,uri = uri"http://localhost:8080/distances")
              .withEntity(DistanceRequestDTO(List("JFK","LHR")))
          )
          expected = DistanceReplyDTO(5000)
        } yield assert(response) {
          equalTo(expected)
        }
      }
    ).provideCustomLayerShared(httpServer)
}

测试的输出是第二个测试失败而第一个成功。 而且我进行了足够的调试,以确保在第二次测试之前已经关闭了HTTPServer。

stopped with exitCode: ()
- MainTest
  + Health check
  - Distances endpoint check
    Fiber failed.
    A checked error was not handled.
    org.http4s.client.UnexpectedStatus: unexpected HTTP status: 404 Not Found

无论我是否在sbt test上从Intellij运行测试,在所有这些操作之后测试过程都保持挂起状态,我必须手动终止它。

解决方法

我认为这里有两件事:

Z管理并获取

ZManaged.make的第一个参数是创建资源的acquire函数。问题在于资源获取(以及释放它们)是不间断的。而且,每当执行.fork时,分叉光纤都会从其父光纤继承其可中断性。因此Main.run()部分实际上永远不会被中断。

为什么fiber.interruptFork似乎起作用? interruptFork实际上并不等待光纤中断。只有interrupt会这样做,这就是为什么它将挂起测试的原因。

幸运的是,有一种方法可以完全满足您的需求:Main.run(List()).forkManaged。这将生成一个ZManaged,它将启动主函数并在释放资源时中断它。

以下代码很好地说明了问题:

import zio._
import zio.console._
import zio.duration._

object Main extends App {

  override def run(args: List[String]): URIO[ZEnv,ExitCode] = for {
    // interrupting after normal fork
    fiberNormal <- liveASecond("normal").fork
    _           <- fiberNormal.interrupt

    // forking in acquire,interrupting in relase
    _ <- ZManaged.make(liveASecond("acquire").fork)(fiber => fiber.interrupt).use(_ => ZIO.unit)

    // fork into a zmanaged
    _ <- liveASecond("forkManaged").forkManaged.use(_ => ZIO.unit)

    _ <- ZIO.sleep(5.seconds)
  } yield ExitCode.success

  def liveASecond(name: String) = (for {
    _ <- putStrLn(s"born: $name")
    _ <- ZIO.sleep(1.seconds)
    _ <- putStrLn(s"lived one second: $name")
    _ <- putStrLn(s"died: $name")
  } yield ()).onInterrupt(putStrLn(s"interrupted: $name"))

}

这将给出输出:

born: normal
interrupted: normal

born: acquire
lived one second: acquire
died: acquire

born: forkManaged
interrupted: forkManaged

如您所见,normalforkManaged都立即被打断。但是acquire中分叉的那一个就可以完成。

第二次考试

第二个测试似乎失败不是因为服务器已关闭,而是因为服务器似乎缺少了http4s端的“距离”路由。我注意到您收到404,这是HTTP状态代码。如果服务器关闭,您可能会得到类似Connection Refused的信息。收到404时,实际上是一些HTTP服务器正在应答。

因此,我的猜测是这条路线确实缺失。也许要在路线定义中检查拼写错误,或者可能是该路线没有组成主要路线。

,

最后,@ felher的Main.run(List()).forkManaged帮助解决了第一个问题。

关于GET从集成测试内部拒绝主体的第二个问题是通过将方法更改为POST来解决的。我没有进一步研究为什么从测试内部拒绝GET的原因,但是当对正在运行的应用程序进行正常卷曲时,我却没有这样做。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res