'A test is using time, but is not advancing the test clock' 在 ZIO 测试中

'A test is using time, but is not advancing the test clock' in ZIO Test

将我的测试迁移到 RC18 后,我收到以下警告并且测试挂起:

Warning: A test is using time, but is not advancing the test clock, which may result in the test hanging. Use TestClock.adjust to manually advance the time.

我有以下测试:

val testLayer: ZLayer[Live, Nothing, Loggings with Blocking with Clock] = (Console.live >>> loggings.consoleLogger) ++ Blocking.live ++ TestClock.default

testM("curl on invalid URL") {
          for {
            fork <- composer.curl("https://bad.xx", 1).flip.fork
            _ <- TestClock.adjust(3.second * 2)
            r <- fork.join
          } yield
            assert(r)(isSubtype[DockerComposerException](hasField("msg", _.msg.trim, equalTo("https://bad.xx could not be reached!"))))
        }.provideCustomLayer(testLayer)

我怀疑我创建的层有误,因为这是我为迁移所做的唯一更改。

文档中的标准测试也失败了(时间为 0):

testM("One can move time very fast") {
  for {
    startTime <- currentTime(TimeUnit.SECONDS)
    _         <- TestClock.adjust(Duration.fromScala(1 minute))
    endTime   <- currentTime(TimeUnit.SECONDS)
  } yield assert(endTime - startTime)(isGreaterThanEqualTo(60L))
}

当我像这样定义图层时:

val testLayer: ZLayer[Live, Nothing, Loggings with Blocking with Clock] = (Console.live >>> loggings.consoleLogger) ++ Blocking.live ++ Clock.live

时间完全没有调整

这是我要测试的代码:

  def curl(host: String, attempt: Int = 200): ZIO[Loggings with Clock, Throwable, Unit] = {
    ZIO.effect(
      Process(Seq("curl", "--output", "/dev/null", "--silent", "--head", "--fail", host)).!!
    ).flatMap(r =>
      info(s"\n$host is ready to use") *> ZIO.succeed()
    ).catchAll(t =>
      if (attempt == 0)
        ZIO.fail(DockerComposerException(s"\n$host could not be reached!", Some(t)))
      else
        info(s"still waiting ;(") *>
          ZIO.sleep(3.second) *>
          curl(host, attempt - 1)
    )
  }

所以我想快进ZIO.sleep(3.seconds)

您需要在 adjust(duration) 之后调用 sleep(duration),以便在调用 currentTime 时反映调整后的时间。所以上面例子的正确版本是:

testM("One can move time very fast") {
  for {
    startTime <- currentTime(TimeUnit.SECONDS)
    _         <- TestClock.adjust(1.minute)
    _         <- ZIO.sleep(1.minute)
    endTime   <- currentTime(TimeUnit.SECONDS)
  } yield assert(endTime - startTime)(isGreaterThanEqualTo(60L))
}

请注意,这反映在当前版本的文档中 here 但尚未反映在网站上,因为我们目前仅在发布时发布更改。

不过,我认为这不是你的问题,因为看起来你的效果只是在使用睡眠。

我无法完全重现您的代码,但以下稍微简化的示例可以正常工作:

import zio._
import zio.clock.Clock
import zio.console.Console
import zio.duration._
import zio.test._
import zio.test.environment.TestClock

object ExampleSpec extends DefaultRunnableSpec {

  type Logging = Has[Logging.Service]
  object Logging {
    trait Service {
      def logLine(line: String): UIO[Unit]
    }
    val live: ZLayer[Console, Nothing, Logging] =
      ZLayer.fromService { console =>
        new Logging.Service {
          def logLine(line: String): UIO[Unit] =
            console.putStrLn(line)
        }
      }
    def logLine(line: String): ZIO[Logging, Nothing, Unit] =
      ZIO.accessM(_.get.logLine(line))
  }

  def effect(n: Int): ZIO[Clock with Logging, String, Unit] =
    if (n == 0) ZIO.fail("fail")
    else Logging.logLine("retrying") *> ZIO.sleep(3.seconds) *> effect(n -1)


  def spec = suite("ExampleSpec") {
    testM("test") {
      for {
        fiber <- effect(1).flip.fork
        _     <- TestClock.adjust(6.seconds)
        _     <- fiber.join
      } yield assertCompletes
    }.provideCustomLayer(Logging.live)
  }
}

您的测试中是否还有其他问题?