microshed-testing icon indicating copy to clipboard operation
microshed-testing copied to clipboard

Ensure ordering to containers

Open KyleAure opened this issue 6 years ago • 3 comments
trafficstars

User Story

As a user of this test framework, I want to be able to pass information from my test-container to my app container during the setup process. This would require a way to define what order containers should be created.

Additional information

If I am using a database test-container:

    @Container
    public static OracleContainer oracle = new OracleContainer();

When I set up my application I want to be able to grab the data-source configuration information from this container.

    @Container
    public static MicroProfileApplication<?> app = new MicroProfileApplication<>()
        .withEnv("NAME", oracle.getUsername())
        .withEnv("PASSWORD", oracle.getPassword())
        .withEnv("URL", oracle.getJdbcUrl())
        .dependsOn(oracle);

In this case the following error is thrown - [ERROR] it.io.openliberty.guides.event.EventEntityTest Time elapsed: 2.823 s <<< ERROR! java.lang.ExceptionInInitializerError Caused by: java.lang.IllegalStateException: Mapped port can only be obtained after the container is started

There is a .dependsOn() method in the test-container spec. But it does not seem to work in some cases. Possibly related to issue: https://github.com/testcontainers/testcontainers-java/issues/1722

KyleAure avatar Aug 19 '19 18:08 KyleAure

Yes currently dependsOn is not usable with parallel start. What can be done instead for now is to use a SharedContainerConfiguration and override the start() method to start containers in the desired ordering.

aguibert avatar Aug 19 '19 18:08 aguibert

I did override that method and still got the same mapped port error. Here is my test class: https://github.com/KyleAure/WebSphereApps/blob/master/system-test-jdbc/src/test/java/it/io/openliberty/guides/event/Containers.java

KyleAure avatar Aug 19 '19 19:08 KyleAure

ah ok, the problem here is that getJdbcUrl() calls getFirstMappedPort() which requires the container to be running. At the time when the public static MicroProfileApplication<?> app field is initialized, no containers are running yet.

So I think the options for workaround this problem are:

  1. Change the container start procedure to not call getJdbcUrl() until after the JDBC container is started like this:
    public void startContainers() {
        oracle.start();
        app.withEnv("URL", oracle.getJdbcUrl());
        app.start();
    }
  1. Use a hardcoded string value when the field is declared:
    @Container
    public static MicroProfileApplication<?> app = new MicroProfileApplication<>()
         .withEnv("NAME", "system")
         .withEnv("PASSWORD", "oracle")
         .withEnv("URL", "jdbc:oracle:thin:system/oracle@localhost:1521:xe")
         .withAppContextRoot("/jpa")
         .withReadinessPath("/jpa/events")
         .dependsOn(oracle);

A hardcoded port value is OK because when app communicates with oracle it is within a shared Docker network, so the ports can have reliable values. Ports are only randomized as they are exposed outside of the docker network (e.g. to the test client JVM).

However, I'm not super fond of either of these options. Perhaps if we had a GenericContainer.withEnv(String key, Supplier<String> value) method that was lazily evaluated, this would be a bit cleaner. Then we could simply do:

    @Container
    public static MicroProfileApplication<?> app = new MicroProfileApplication<>()
        .withEnv("NAME", oracle.getUsername())
        .withEnv("PASSWORD", oracle.getPassword())
        .withEnv("URL", () -> oracle.getJdbcUrl())
        .withAppContextRoot("/jpa")
        .withReadinessPath("/jpa/events")
        .dependsOn(oracle);

aguibert avatar Aug 19 '19 21:08 aguibert