microshed-testing
microshed-testing copied to clipboard
Ensure ordering to containers
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
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.
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
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:
- 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();
}
- 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);