0

I have gone thru' multiple blogs and official documentation but couldn't resolve my issue. I am using testContainers-scala version 0.38.1 and scala version 2.11.

I am trying to create a simple test using testContainer-scala as below:

class MyServiceITSpec extends AnyFlatSpec with ForAllTestContainer {
  override val container = GenericContainer(dockerImage="my-service",
    exposedPorts = Seq(8080),
    env=(HashMap[String, String]("PARAM1" -> "value1", "PARAM2" -> "value2", "PARAM3" -> "value3")),
    waitStrategy = Wait.forHttp("/")
  )

  "GenericContainer" should "start my service and say Hello! Wassupp" in {
    assert(Source.fromInputStream(
      new URL(s"http://${container.containerIpAddress}:${container.mappedPort(8080)}/").openConnection().getInputStream
    ).mkString.contains("Hello! Wassupp"))
  }
}

On the basis of the above snippet, my understanding is this (please correct if wrong):

  1. Port 8155 is exposed by the docker container and a random host port against the same would be assigned.
  2. We can get that assigned port as container.mappedPort

Here I am trying to assert that http:// localhost:mappedPort/ return Hello! Wassupp.

But, I get the below error:

Caused by: org.testcontainers.containers.ContainerLaunchException: Could not create/start container
  at org.testcontainers.containers.GenericContainer.tryStart(GenericContainer.java:498)
  at org.testcontainers.containers.GenericContainer.lambda$doStart$0(GenericContainer.java:325)
  at org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess(Unreliables.java:81)
  ... 18 more
Caused by: org.testcontainers.containers.ContainerLaunchException: Timed out waiting for URL to be accessible (http://localhost:32869/ should return HTTP 200)
  at org.testcontainers.containers.wait.strategy.HttpWaitStrategy.waitUntilReady(HttpWaitStrategy.java:214)
  at org.testcontainers.containers.wait.strategy.AbstractWaitStrategy.waitUntilReady(AbstractWaitStrategy.java:35)
  at org.testcontainers.containers.GenericContainer.waitUntilContainerStarted(GenericContainer.java:890)
  at org.testcontainers.containers.GenericContainer.tryStart(GenericContainer.java:441)
  ... 20 more

The same image runs just fine with:

docker run -p 8081:8080 -e PARAM1=value1 -e PARAM2=value2 -e PARAM3=VALUE3 my-service
Mario Galic
  • 47,285
  • 6
  • 56
  • 98
Karttik Mishra
  • 150
  • 2
  • 14
  • 1
    Are you sure that you application returns http code 200? waitStrategy = Wait.forHttp("/") implies that an http response should be returned. – Vitaly Chura Jul 23 '20 at 08:28
  • Yes, it does. However, I found that the root of this problem is missing Headers. Adding the answer below for reference for anyone who may run into similar issues. – Karttik Mishra Jul 23 '20 at 14:09

1 Answers1

1

So after juggling with the errors, I found my issue. It is to do with the required Request Headers missing from the request. I am adding the reference code for anyone who runs into similar issue.

import com.dimafeng.testcontainers.{ForAllTestContainer, GenericContainer}
import org.scalatest.flatspec.AnyFlatSpec
import org.testcontainers.containers.wait.strategy.Wait

import scala.collection.immutable.HashMap
import scalaj.http.Http



    class MyServiceITSpec extends AnyFlatSpec with ForAllTestContainer {
      override val container = GenericContainer(dockerImage="my-service-img:tag12345",
        exposedPorts = Seq(8080),
        env=(HashMap[String, String]("PARAM1" -> "value1", "PARAM2" -> "value2")),
        waitStrategy = Wait.forHttp("/") // or "/health" based on ur implementation 
      )
    
      "My Service" should "successfully fetch the msg" in {
        assert(Http(s"http://${container.containerIpAddress}:${container.mappedPort(8080)}/products/product1")
          .header("HEADER1", "value1")
          .header("HEADER2", "value2")
          .asString.code==200)
      }
    
    }

Some explanations that I found after a lot of reading:

  1. You give the port number that your docker application exposes as exposedPorts.
  2. TestContainers then does a mapping of this port against a random port (this is by design to avoid port number conflicts). If you were to run this docker image directly on your machine you would write:

docker run -p 8081:8080 -e PARAM1=value1 -e PARAM2=value2 my-service-img:tag12345

Here, your exposed port is 8080 and the mapped port is 8081.

  1. TestContainers runs the docker image by exposing the port 8080 and then mapping it against a random port. The mapped port can be container.mappedPort().

  2. Another important thing to notice is the wait strategy. This tells the code to wait unless the / endpoint gets up. This is kind of a health check that your application exposes. You can have a better endpoint for the same - like /health. By default, it waits for 60 seconds for this endpoint to become active. Post that it would anyway run the tests and if the application has not started by then, it would cause an error. I am not sure how to override the default timeout but I think there should be a way to do that.

  3. Lastly, I am using scalaj.http.Http to make a HTTP request(it is a pretty easy one to use - you can ).

Karttik Mishra
  • 150
  • 2
  • 14