Search code examples
dockerintegration-testingquarkustestcontainers

How can I connect a test container in Quarkus to the Docker network of DevServices?


In my @QuarkusIntegrationTest I want to use a MockServer testcontainer that simulates the responses to requests from my application. To start the MockServer I use testcontainers. The application uses http://localhost:port, where port is mockserverContainer.getMappedPort (). It all works.

When I test the application with -Dquarkus.container-image.build=true, I get the following problem: Quarkus creates a Docker network and connects the container with the application and the MongoDb test container to this network via the DevServices. I would also like to bring the MockServer test container into this network, but I can't. Network.SHARED that is available in the test is not the network that Quarkus uses for the DevServices. Of course, I can use the host's IP address to access the MockServer from the application that is being tested, which complicates the build scripts because the host's IP address is not that easy to determine in every environment.

Is there any way that the testcontainer can connect to the Docker network used by the Quarkus DevService?

(Quarkus Version 2.3.0)

The following example works with the -Dquarkus.container-image.build=false option, i.e. if the application itself is not running in a container.

@QuarkusIntegrationTest
@Testcontainers
public class JobTest {

    @Container
    public static MockServerContainer serverContainer = new MockServerContainer()

    @BeforeAll
    public static initMockServer() {
      String json = ...
      new MockServerClient(serverContainer.getHost(), serverContainer.getServerPort())
          .when(request()
              .withMethod("GET")
              .withPath(".*"))
          .respond(response()
              .withStatusCode(Response.Status.OK.getStatusCode())
              .withHeader(CONTENT_TYPE, MediaType.APPLICATION_JSON)
              .withBody(json));
    }

    @Test
    @DisplayName("import recipe from website")
    public void importRecipe() {
        // The URL that the application wants to get data from. Is simulated by the MockServer. 
        var recipeUrl = String.format("http://localhost:%d/lasagne.html", serverContainer.getServerPort());
        // The URL of the application that triggered the import function. 
        var jobUrl = String.format("http://localhost:%s/job", System.getProperty("quarkus.http.port"));
        RestAssured
                .given()
                .body(createJob(recipeUrl))
                .contentType(ContentType.JSON)
                .when()
                .post(jobUrl)
                .then()
                .statusCode(Response.Status.CREATED.getStatusCode())
                .header("Location", r -> equalTo(url + "/" + r.path("jobId")));
    }
}


Solution

  • As of Quarkus 2.5, you will be able to do something like:

    public class CustomResource implements QuarkusTestResourceLifecycleManager, DevServicesContext.ContextAware {
    
        private Optional<String> containerNetworkId = Optional.empty(); 
    
        @Override
        public Map<String, String> start() {
            // start a container here using containerNetworkId
            return someMap;
        }
    
        @Override
        public void stop() {
            // close the container
        }
    
    
        @Override
        public void setIntegrationTestContext(DevServicesContext context) {
            containerNetworkId = context.containerNetworkId();
        }
    }
    

    and your test would be:

    @QuarkusIntegrationTest
    @QuarkusTestResource(CustomResource.class)
    public class JobTest {
      
    
       ...
    
    
    }
    

    See this for more details.