You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法通过TestClock推进测试ZIO HTTP客户端指数重试

ZIO HTTP客户端测试挂起问题分析

环境依赖

  • ZIO HTTP 3.0.0-RC6(手动排除了间接引入的ZIO 2.0.21)
  • ZIO 与 ZIO-test 2.1-RC1
  • Scala 2.13

测试套件代码

object IpGetterSpec extends ZIOSpecDefault {
  val schedule = Schedule.exponential(10.milliseconds) && 
      Schedule.recurs(2).tapOutput(o => ZIO.logInfo(s"retrying $o"))
  def spec = suite("IpGetterSpec")(
    test("test retry exponential") {
      for {
        fiber <- ZIO.fail("fail").retry(schedule).fork
        _ <- TestClock.adjust(100.milliseconds)
        res <- fiber.join.exit
      } yield assertTrue(res.isFailure)
    },
    test("request fails after being tried three times") {
      for {
        client <- ZIO.service[Client]
        fiber  <- client.url(URL.decode("http://127.0.0.1:80/?format=json").toOption.get).get("/").retry(schedule).fork
        _ <- TestClock.adjust(100.milliseconds)
        res <- fiber.join.exit
      } yield assertTrue(res.isFailure)
    }
  ).provideShared(Client.default, Scope.default)
}

现象

  • 第一个测试(纯ZIO失败重试逻辑验证)可正常运行,符合预期
  • 第二个测试(验证HTTP请求重试逻辑,目标地址127.0.0.1:80无服务监听)使用TestClock.adjust时无限挂起
  • 将第二个测试改为使用实时时钟后,测试可正常运行:
test("request fails after being tried three times") {
  for {
    client <- ZIO.service[Client]
    res  <- client.url(URL.decode("http://127.0.0.1:80/?format=json").toOption.get).get("/").retry(schedule).exit
  } yield assertTrue(res.isFailure)
} @@ TestAspect.withLiveClock

问题解答

  • 该现象确实与ZIO HTTP客户端有关,属于已知的测试环境交互问题
  • 核心原因:ZIO HTTP客户端的底层网络操作(如TCP连接建立)依赖JDK原生IO实现,这类操作不受ZIO TestClock的管控。当使用TestClock调整时间时,重试调度的时间触发逻辑仅作用于ZIO层,但实际的网络连接尝试不会被TestClock模拟,导致fiber一直等待网络操作的结果返回,最终表现为无限挂起。
  • 使用TestAspect.withLiveClock时,重试调度基于真实时间触发,网络连接失败的错误能被正常捕获,重试逻辑按预期执行3次后失败,测试顺利完成。

内容的提问来源于stack exchange,提问作者Yar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 21:32:59