From 9d359effcdf5d021702fa357d590ec1b60c5757c Mon Sep 17 00:00:00 2001 From: Christian Wansart Date: Sat, 11 Apr 2020 19:36:25 +0200 Subject: [PATCH 1/4] Add documentation for reusable containers. --- .../ReusableContainerWithJdbcUrlsTest.java | 45 ++++++++++ .../java/generic/ReusableContainersTest.java | 24 ++++++ .../generic/ReusableKafkaContainerTest.java | 25 ++++++ docs/features/reusable_containers.md | 86 +++++++++++++++++++ mkdocs.yml | 1 + 5 files changed, 181 insertions(+) create mode 100644 docs/examples/junit4/generic/src/test/java/generic/ReusableContainerWithJdbcUrlsTest.java create mode 100644 docs/examples/junit4/generic/src/test/java/generic/ReusableContainersTest.java create mode 100644 docs/examples/junit4/generic/src/test/java/generic/ReusableKafkaContainerTest.java create mode 100644 docs/features/reusable_containers.md diff --git a/docs/examples/junit4/generic/src/test/java/generic/ReusableContainerWithJdbcUrlsTest.java b/docs/examples/junit4/generic/src/test/java/generic/ReusableContainerWithJdbcUrlsTest.java new file mode 100644 index 00000000000..09fdcfb9d4a --- /dev/null +++ b/docs/examples/junit4/generic/src/test/java/generic/ReusableContainerWithJdbcUrlsTest.java @@ -0,0 +1,45 @@ +package generic; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.GenericContainer; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.time.Duration; +import java.time.Instant; + +import static org.junit.Assert.assertTrue; + +public class ReusableContainerWithJdbcUrlsTest { + + private static final Logger LOG = LoggerFactory.getLogger(ReusableContainerWithJdbcUrlsTest.class); + + private static GenericContainer database; + + @BeforeClass + public static void setUp() { + database = new GenericContainer<>("ibmcom/db2:11.5.0.0a") + .withReuse(true); + database.start(); + } + + @Test + public void testKafkaContainer() throws SQLException { + assertTrue(database.isRunning()); + + Instant startedAt = Instant.now(); + Connection connection = DriverManager.getConnection( + "jdbc:tc:db2:///?TC_REUSABLE=true" + ); + + boolean execute = connection.createStatement().execute("SELECT 1 FROM SYSIBM.SYSDUMMY1"); + + LOG.info("Total test time: {}", Duration.between(startedAt, Instant.now())); + + assertTrue(execute); + } +} diff --git a/docs/examples/junit4/generic/src/test/java/generic/ReusableContainersTest.java b/docs/examples/junit4/generic/src/test/java/generic/ReusableContainersTest.java new file mode 100644 index 00000000000..d0987eee576 --- /dev/null +++ b/docs/examples/junit4/generic/src/test/java/generic/ReusableContainersTest.java @@ -0,0 +1,24 @@ +package generic; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +import static org.junit.Assert.assertTrue; + +public class ReusableContainersTest { + + private static GenericContainer nginx; + + @BeforeClass + public static void setUp() { + nginx = new GenericContainer<>("nginx:1.17.9") + .withExposedPorts(80) + .withReuse(true); + } + + @Test + public void testContainersAllStarted() { + assertTrue(nginx.isRunning()); + } +} diff --git a/docs/examples/junit4/generic/src/test/java/generic/ReusableKafkaContainerTest.java b/docs/examples/junit4/generic/src/test/java/generic/ReusableKafkaContainerTest.java new file mode 100644 index 00000000000..065bd433849 --- /dev/null +++ b/docs/examples/junit4/generic/src/test/java/generic/ReusableKafkaContainerTest.java @@ -0,0 +1,25 @@ +package generic; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.testcontainers.containers.KafkaContainer; + +import static org.junit.Assert.assertTrue; + +public class ReusableKafkaContainerTest { + + private static KafkaContainer kafka; + + @BeforeClass + public static void setUp() { + kafka = new KafkaContainer() + .withNetwork(null) + .withReuse(true); + kafka.start(); + } + + @Test + public void testKafkaContainer() { + assertTrue(kafka.isRunning()); + } +} diff --git a/docs/features/reusable_containers.md b/docs/features/reusable_containers.md new file mode 100644 index 00000000000..c4d0d638dca --- /dev/null +++ b/docs/features/reusable_containers.md @@ -0,0 +1,86 @@ +# Reusable containers (ALPHA) + +!!! warning "Reusable containers are still an alpha feature" + + Reusable containers are still an **ALPHA** feature. It has not been finished yet. The current state can be found in + the pull request [#1781](https://github.com/testcontainers/testcontainers-java/pull/1781). + +In contrast to [singleton containers](../test_framework_integration/manual_lifecycle_control.md/#singleton-containers) +reusable containers survive the end of the tests. This may speed up consecutive test runs. + +## What reusable containers can do + + * Reusable containers support hashing. This is used to detect configuration changes. // TODO: please check if this is correct + * JDBC url support. + * Check for overrides like `containerIsCreated`. // TODO: I am not quite sure if I understand what this means and what it is used for. + * Per environment enablement. This feature has to be enabled twice, via property and local testcontainers configuration. + +## What reusable cannot do + +Hence this is an alpha feature it is not yet fully implemented. + + * Locking for parallel tests are not supported yet. Tests with the same hash will use the same container. + * Containers will not be deleted automatically. The user has to stop and delete them manually. (Check `docker ps` after + running a test with reusable containers.) + * There is no clean up mechanism when the configuration changes. This will start a new container. + * *Started* successfully marker to avoid race conditions. // TODO: I am not sure what the difference to the overrides part with `containerIsCreated` is. + * It is not possible to start the containers via JUnit 4's or JUnit 5's `@Rule` and `@Container` annotations. // TODO: is this correct? As far as I understand the config changes and the container will not be reused. + +## Prerequisites + +There is one essential prerequisite: `testcontainers.reuse.enable=true` needs to be set in your local +testcontainers.properties file. The location depends on the machine and can be found in the +[configuration file location](./configuration/#configuration-file-location) documentation. + +If tests are run without the configuration but the `withReuse(true)` was set on the container, there will be a warning: + +> Reuse was requested but the environment does not support the reuse of containers + +## Starting a reusable containers + +In order to create a test tha uses reusable containers you need to create it somewhere you can start it manually. For +example in JUnit 4 you can create a static instance of a container and use a `@BeforeClass` annotated set-up method to +instantiate and start the container. + + +[Using reusable containers](../examples/junit4/generic/src/test/java/generic/ReusableContainersTest.java) inside_block:single_label + + +After running this test once, you can see that the container survived the end of the test execution with `docker ps`. +You will find a `nginx` container there. When running the test another time the container will be reused this avoiding +start-up time. +Another benefit is, if you have multiple tests in different test files which require the same container regardless of +it's current state, it can be reused by initializing it the same as before. + +## Examples + +Beside generic containers you can also use the specialized containers. Here are some examples. + +// THIS EXAMPLES HAS BEEN COPIED AND ADJUSTED FOR THE USE HERE. @Sergei, I hope your okay with this, those examples +// show diverse use of the current state of reusable containers. + +### Example usage with container objects + + +[Using reusable containers](../examples/junit4/generic/src/test/java/generic/ReusableKafkaContainerTest.java) inside_block:single_label + + +Here we unset the network that was implicitly created by KafkaContainer (but not used in this case), because otherwise +the network id will always be random and affect the hash. Kafka is an exceptional case (to be fixed, left for backward +compatibility) and most of other containers do not set the network implicitly. + +### Example usage with JDBC URLs + + +[Using reusable containers](../examples/junit4/generic/src/test/java/generic/ReusableContainerWithJdbcUrlsTest.java) inside_block:single_label + + +## Ongoing work + +There is still work to do to become a production-ready feature. Some of those additions are: + + * Networks + * Multi container setups + * Automatic deletion of old containers (TTL) + * Cleanup for new container configurations + * Locking for parallel tests diff --git a/mkdocs.yml b/mkdocs.yml index 49876885dfd..7995ee4938d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -37,6 +37,7 @@ nav: - features/creating_images.md - features/configuration.md - features/advanced_options.md + - features/reusable_containers.md - Modules: - Databases: - modules/databases/index.md From 6c5086a93c9e5f333c49326f2d77d93d5ba68d7d Mon Sep 17 00:00:00 2001 From: Christian Wansart Date: Sat, 11 Apr 2020 19:44:05 +0200 Subject: [PATCH 2/4] Fix title for code snippets. --- docs/features/reusable_containers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/reusable_containers.md b/docs/features/reusable_containers.md index c4d0d638dca..89aa4bbbec2 100644 --- a/docs/features/reusable_containers.md +++ b/docs/features/reusable_containers.md @@ -62,7 +62,7 @@ Beside generic containers you can also use the specialized containers. Here are ### Example usage with container objects -[Using reusable containers](../examples/junit4/generic/src/test/java/generic/ReusableKafkaContainerTest.java) inside_block:single_label +[Test using the Kafka container](../examples/junit4/generic/src/test/java/generic/ReusableKafkaContainerTest.java) inside_block:single_label Here we unset the network that was implicitly created by KafkaContainer (but not used in this case), because otherwise @@ -72,7 +72,7 @@ compatibility) and most of other containers do not set the network implicitly. ### Example usage with JDBC URLs -[Using reusable containers](../examples/junit4/generic/src/test/java/generic/ReusableContainerWithJdbcUrlsTest.java) inside_block:single_label +[Test that uses JDBC urls](../examples/junit4/generic/src/test/java/generic/ReusableContainerWithJdbcUrlsTest.java) inside_block:single_label ## Ongoing work From 9e4078f5e608f839842199aa36686d2176c0de8a Mon Sep 17 00:00:00 2001 From: Christian Wansart Date: Sat, 11 Apr 2020 20:53:16 +0200 Subject: [PATCH 3/4] Improve snippets and fix tests. --- docs/examples/junit4/generic/build.gradle | 3 +++ .../generic/ReusableContainerWithJdbcUrlsTest.java | 12 +++++++----- .../test/java/generic/ReusableContainersTest.java | 5 +++++ .../java/generic/ReusableKafkaContainerTest.java | 2 ++ docs/features/reusable_containers.md | 6 +++--- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/examples/junit4/generic/build.gradle b/docs/examples/junit4/generic/build.gradle index 97902d5d9ca..48bf591c099 100644 --- a/docs/examples/junit4/generic/build.gradle +++ b/docs/examples/junit4/generic/build.gradle @@ -5,4 +5,7 @@ dependencies { testCompile project(":testcontainers") testCompile project(":selenium") testCompile "org.seleniumhq.selenium:selenium-api:3.141.59" + testCompile "org.testcontainers:kafka:1.13.0" + testCompile "org.testcontainers:postgresql:1.13.0" + testCompile "org.postgresql:postgresql:42.2.12" } diff --git a/docs/examples/junit4/generic/src/test/java/generic/ReusableContainerWithJdbcUrlsTest.java b/docs/examples/junit4/generic/src/test/java/generic/ReusableContainerWithJdbcUrlsTest.java index 09fdcfb9d4a..8b4b5402830 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ReusableContainerWithJdbcUrlsTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ReusableContainerWithJdbcUrlsTest.java @@ -16,13 +16,14 @@ public class ReusableContainerWithJdbcUrlsTest { - private static final Logger LOG = LoggerFactory.getLogger(ReusableContainerWithJdbcUrlsTest.class); + private static final Logger logger = LoggerFactory.getLogger(ReusableContainerWithJdbcUrlsTest.class); + // jdbc_init { private static GenericContainer database; @BeforeClass public static void setUp() { - database = new GenericContainer<>("ibmcom/db2:11.5.0.0a") + database = new GenericContainer<>("postgres:9.6.17") .withReuse(true); database.start(); } @@ -33,13 +34,14 @@ public void testKafkaContainer() throws SQLException { Instant startedAt = Instant.now(); Connection connection = DriverManager.getConnection( - "jdbc:tc:db2:///?TC_REUSABLE=true" + "jdbc:tc:postgresql:9.6.17:///?TC_REUSABLE=true" ); - boolean execute = connection.createStatement().execute("SELECT 1 FROM SYSIBM.SYSDUMMY1"); + boolean execute = connection.createStatement().execute("SELECT 1"); - LOG.info("Total test time: {}", Duration.between(startedAt, Instant.now())); + logger.info("Total test time: {}", Duration.between(startedAt, Instant.now())); assertTrue(execute); } + // } } diff --git a/docs/examples/junit4/generic/src/test/java/generic/ReusableContainersTest.java b/docs/examples/junit4/generic/src/test/java/generic/ReusableContainersTest.java index d0987eee576..db2eff185e9 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ReusableContainersTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ReusableContainersTest.java @@ -3,19 +3,24 @@ import org.junit.BeforeClass; import org.junit.Test; import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; import static org.junit.Assert.assertTrue; public class ReusableContainersTest { + // reusable_containers { private static GenericContainer nginx; @BeforeClass public static void setUp() { nginx = new GenericContainer<>("nginx:1.17.9") .withExposedPorts(80) + .waitingFor(Wait.forHttp("/")) .withReuse(true); + nginx.start(); } + // } @Test public void testContainersAllStarted() { diff --git a/docs/examples/junit4/generic/src/test/java/generic/ReusableKafkaContainerTest.java b/docs/examples/junit4/generic/src/test/java/generic/ReusableKafkaContainerTest.java index 065bd433849..d6c9d6c8363 100644 --- a/docs/examples/junit4/generic/src/test/java/generic/ReusableKafkaContainerTest.java +++ b/docs/examples/junit4/generic/src/test/java/generic/ReusableKafkaContainerTest.java @@ -8,6 +8,7 @@ public class ReusableKafkaContainerTest { + // kafka_init { private static KafkaContainer kafka; @BeforeClass @@ -17,6 +18,7 @@ public static void setUp() { .withReuse(true); kafka.start(); } + // } @Test public void testKafkaContainer() { diff --git a/docs/features/reusable_containers.md b/docs/features/reusable_containers.md index 89aa4bbbec2..009dcf017d4 100644 --- a/docs/features/reusable_containers.md +++ b/docs/features/reusable_containers.md @@ -43,7 +43,7 @@ example in JUnit 4 you can create a static instance of a container and use a `@B instantiate and start the container. -[Using reusable containers](../examples/junit4/generic/src/test/java/generic/ReusableContainersTest.java) inside_block:single_label +[Using reusable containers](../examples/junit4/generic/src/test/java/generic/ReusableContainersTest.java) inside_block:reusable_containers After running this test once, you can see that the container survived the end of the test execution with `docker ps`. @@ -62,7 +62,7 @@ Beside generic containers you can also use the specialized containers. Here are ### Example usage with container objects -[Test using the Kafka container](../examples/junit4/generic/src/test/java/generic/ReusableKafkaContainerTest.java) inside_block:single_label +[Test using the Kafka container](../examples/junit4/generic/src/test/java/generic/ReusableKafkaContainerTest.java) inside_block:kafka_init Here we unset the network that was implicitly created by KafkaContainer (but not used in this case), because otherwise @@ -72,7 +72,7 @@ compatibility) and most of other containers do not set the network implicitly. ### Example usage with JDBC URLs -[Test that uses JDBC urls](../examples/junit4/generic/src/test/java/generic/ReusableContainerWithJdbcUrlsTest.java) inside_block:single_label +[Test that uses JDBC urls](../examples/junit4/generic/src/test/java/generic/ReusableContainerWithJdbcUrlsTest.java) inside_block:jdbc_init ## Ongoing work From 55f800c39c49a5ec2075b19d40e2e1ea2d83a79a Mon Sep 17 00:00:00 2001 From: Christian Wansart Date: Sat, 11 Apr 2020 21:03:15 +0200 Subject: [PATCH 4/4] Fix typo in heading. --- docs/features/reusable_containers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/reusable_containers.md b/docs/features/reusable_containers.md index 009dcf017d4..fd3eecfcbdc 100644 --- a/docs/features/reusable_containers.md +++ b/docs/features/reusable_containers.md @@ -15,7 +15,7 @@ reusable containers survive the end of the tests. This may speed up consecutive * Check for overrides like `containerIsCreated`. // TODO: I am not quite sure if I understand what this means and what it is used for. * Per environment enablement. This feature has to be enabled twice, via property and local testcontainers configuration. -## What reusable cannot do +## What reusable containers cannot do Hence this is an alpha feature it is not yet fully implemented.