scalaintegration-testingziohttp4szio-test

Integration tests hangs when testing my API in ZIO + HTTP4S


I am having issues testing my first ZIO+HTTP4S application. The test hangs and does not finish.

The code for my App (simplified) is


object Main extends App {
  def server: ZIO[AppEnvironment, Throwable, Unit] =
    for {
      (...)
      fullApp <- ZIO.runtime[AppEnvironment].flatMap { implicit rts =>
        BlazeServerBuilder[AppTask](ec)
          .bindHttp(api.port, api.endpoint)
          .withHttpApp(CORS(httpApp))
          .serve
          .compile[AppTask, AppTask, CatsExitCode]
          .drain
      }
    } yield fullApp

  override def run(args: List[String]): ZIO[ZEnv, Nothing, ExitCode] = {
    server.provideSomeLayer[ZEnv](appEnvironment).tapError(err => putStrLn(s"Execution failed with: $err")).exitCode
  }
}

This is my test code. Note that it is basically copypasted from THIS OTHER STACKOVERFLOW QUESTION


object ApiTest extends DefaultRunnableSpec {
  val ec: ExecutionContext = ExecutionContext.global
  def httpServer           = Main.run(List()).forkManaged.toLayer

  val clockDuration = ofSeconds(1)
  val blocker       = Blocker.liftExecutionContext(ec)

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

  override def spec: ZSpec[Environment, Failure] =
    suite("MainTest")(
      testM("Health check") {
        for {
          _ <- TestClock.adjust(clockDuration).fork
          _ = println("1")
          _ <- isLocalPortInUse(8080)
          _ = println("2")
          client <- Task(JavaNetClientBuilder[Task](blocker).create)
          _ = println("3")
          response <- client.expect[String]("http://localhost:8080/healthcheck")
          _ = println("4")
        } yield assert(response)(equalTo(""))
      }
    ).provideCustomLayerShared(httpServer)
}

The problem is that as soon as the server starts, the test stops running and are not executed. The output is

1
checking for local port in use
checking for local port in use
<server init message>
In Suite "MainTest", test "Health check" has taken more than 1 m to execute. If this is not expected, consider using TestAspect.timeout to timeout runaway tests for faster diagnostics.

So as you can see, the tests run OK until the server starts and then does not continue the execution.

Also, how could I perform a POST call instead of a GET one? I'm a bit lost in the HTTP4S/ZIO ecosystem with http4sClients, ZHTTP, BlazeClientBuilders and the like. What would be the easiest way of doing a POST call to my server in a test like the previous one?

Cheers!

edit: I’ve checked that the server works fine while hanging in here, I can do CURL calls from a terminal and it responds correctly. So it seems clear that the problem is that as soon as the server is up it stays in the front, not background, and the tests do not have the chance to finish the execution.


Solution

  • You are advancing the clock by 1 second but your application might require more time to run. Also, your particular test will require infinite time to run because, while unit tests are instantaneous in ZIO, integration tests are not.

    Advancing the time of a unit test by 1 second requires theoretically 0 seconds. This might not be enough for the port to become free.

    Since you are trying to create an integration test, you should use a real Clock and not the one provided by the test kit.