[ 
https://issues.apache.org/jira/browse/CAMEL-12534?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16501887#comment-16501887
 ] 

ASF GitHub Bot commented on CAMEL-12534:
----------------------------------------

lburgazzoli closed pull request #2362: CAMEL-12534: create camel-testcontainers
URL: https://github.com/apache/camel/pull/2362
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/apache-camel/pom.xml b/apache-camel/pom.xml
index 51c2868ac56..36f972e0867 100644
--- a/apache-camel/pom.xml
+++ b/apache-camel/pom.xml
@@ -1097,6 +1097,14 @@
       <groupId>org.apache.camel</groupId>
       <artifactId>camel-test-spring</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-testcontainers</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-testcontainers-spring</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.camel</groupId>
       <artifactId>camel-testng</artifactId>
diff --git a/apache-camel/src/main/descriptors/common-bin.xml 
b/apache-camel/src/main/descriptors/common-bin.xml
index d8b53a81bee..d1b898b5ae5 100644
--- a/apache-camel/src/main/descriptors/common-bin.xml
+++ b/apache-camel/src/main/descriptors/common-bin.xml
@@ -283,6 +283,8 @@
         <include>org.apache.camel:camel-test-cdi</include>
         <include>org.apache.camel:camel-test-karaf</include>
         <include>org.apache.camel:camel-test-spring</include>
+        <include>org.apache.camel:camel-testcontainers</include>
+        <include>org.apache.camel:camel-testcontainers-spring</include>
         <include>org.apache.camel:camel-testng</include>
         <include>org.apache.camel:camel-thrift</include>
         <include>org.apache.camel:camel-tika</include>
diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index 3402e64650f..129131751b6 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -2623,6 +2623,16 @@
         <artifactId>camel-test-spring</artifactId>
         <version>${project.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-testcontainers</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-testcontainers-spring</artifactId>
+        <version>${project.version}</version>
+      </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
         <artifactId>camel-testng</artifactId>
diff --git a/components/camel-consul/pom.xml b/components/camel-consul/pom.xml
index e5807ec6fc3..94a578dbc40 100644
--- a/components/camel-consul/pom.xml
+++ b/components/camel-consul/pom.xml
@@ -56,12 +56,7 @@
     <!-- testing -->
     <dependency>
       <groupId>org.apache.camel</groupId>
-      <artifactId>camel-test</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.camel</groupId>
-      <artifactId>camel-test-spring</artifactId>
+      <artifactId>camel-testcontainers-spring</artifactId>
       <scope>test</scope>
     </dependency>
     <dependency>
@@ -104,14 +99,6 @@
       <artifactId>log4j-slf4j-impl</artifactId>
       <scope>test</scope>
     </dependency>
-
-    <!-- testing (docker) -->
-    <dependency>
-      <groupId>org.testcontainers</groupId>
-      <artifactId>testcontainers</artifactId>
-      <version>${testcontainers-version}</version>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
 
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulCatalogTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulCatalogTest.java
index 8986048009e..fa09d1f1cdc 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulCatalogTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulCatalogTest.java
@@ -21,7 +21,6 @@
 import com.orbitz.consul.model.health.Node;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.consul.endpoint.ConsulCatalogActions;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
 import org.junit.Assert;
 import org.junit.Test;
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulClientKeyValueTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulClientKeyValueTest.java
index 360157d7c02..9c6bd40b5fb 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulClientKeyValueTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulClientKeyValueTest.java
@@ -20,7 +20,6 @@
 
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.consul.endpoint.ConsulKeyValueActions;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.impl.JndiRegistry;
 import org.junit.Test;
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulCoordinatesTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulCoordinatesTest.java
index e2a0bef97db..96b485f7526 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulCoordinatesTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulCoordinatesTest.java
@@ -22,7 +22,6 @@
 import com.orbitz.consul.model.coordinate.Datacenter;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.consul.endpoint.ConsulCoordinatesActions;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
 import org.junit.Assert;
 import org.junit.Ignore;
 import org.junit.Test;
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulEventTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulEventTest.java
index 82c1ca18f87..68e30cf56be 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulEventTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulEventTest.java
@@ -22,7 +22,6 @@
 import com.orbitz.consul.model.event.Event;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.consul.endpoint.ConsulEventActions;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.junit.Test;
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulEventWatchTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulEventWatchTest.java
index 368d0f40412..52c98623232 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulEventWatchTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulEventWatchTest.java
@@ -20,7 +20,6 @@
 
 import com.orbitz.consul.EventClient;
 import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.junit.Test;
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulHealthTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulHealthTest.java
index d9e48aef8ae..013f2901a6e 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulHealthTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulHealthTest.java
@@ -27,7 +27,6 @@
 import com.orbitz.consul.model.health.ServiceHealth;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.consul.endpoint.ConsulHealthActions;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -42,6 +41,8 @@
 
     @Override
     public void doPreSetup() throws Exception {
+        super.doPreSetup();
+
         Random random = new Random();
 
         this.service = UUID.randomUUID().toString();
@@ -62,12 +63,12 @@ public void doPreSetup() throws Exception {
         );
 
         this.registrations.forEach(client::register);
-        super.doPreSetup();
     }
 
     @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
+    public void doPostTearDown() throws Exception {
+        super.doPostTearDown();
+
         registrations.forEach(r -> client.deregister(r.getId()));
     }
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulKeyValueTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulKeyValueTest.java
index c8703119c45..a0e4f056073 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulKeyValueTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulKeyValueTest.java
@@ -20,7 +20,6 @@
 
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.consul.endpoint.ConsulKeyValueActions;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.junit.Test;
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulKeyValueWatchTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulKeyValueWatchTest.java
index 8fbebe5ab26..71dfbf5b526 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulKeyValueWatchTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulKeyValueWatchTest.java
@@ -21,7 +21,6 @@
 
 import com.orbitz.consul.KeyValueClient;
 import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.junit.Test;
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulRegistryTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulRegistryTest.java
index 7c04165f9d5..a026d2b87e1 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulRegistryTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulRegistryTest.java
@@ -24,7 +24,6 @@
 
 import com.orbitz.consul.Consul;
 import org.apache.camel.NoSuchBeanException;
-import org.apache.camel.component.consul.support.ConsulContainerSupport;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -53,7 +52,7 @@ public String hello(String name) {
 
     @BeforeClass
     public static void setUp() {
-        container = ConsulContainerSupport.consulContainer();
+        container = ConsulTestSupport.consulContainer();
         container.start();
 
         registry = new ConsulRegistry(container.getContainerIpAddress(), 
container.getMappedPort(Consul.DEFAULT_HTTP_PORT));
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulSessionTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulSessionTest.java
index 30ff056085e..1e3b7ab8808 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulSessionTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulSessionTest.java
@@ -24,7 +24,6 @@
 import com.orbitz.consul.model.session.SessionInfo;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.consul.endpoint.ConsulSessionActions;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
 import org.junit.Assert;
 import org.junit.Test;
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/support/ConsulTestSupport.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulTestSupport.java
similarity index 66%
rename from 
components/camel-consul/src/test/java/org/apache/camel/component/consul/support/ConsulTestSupport.java
rename to 
components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulTestSupport.java
index 8c93800e462..9675ed98799 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/support/ConsulTestSupport.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/ConsulTestSupport.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.component.consul.support;
+package org.apache.camel.component.consul;
 
 import java.util.Arrays;
 import java.util.List;
@@ -22,22 +22,21 @@
 
 import com.orbitz.consul.Consul;
 import com.orbitz.consul.KeyValueClient;
-import org.apache.camel.component.consul.ConsulComponent;
 import org.apache.camel.impl.JndiRegistry;
-import org.apache.camel.test.junit4.CamelTestSupport;
+import org.apache.camel.test.testcontainers.ContainerAwareTestSupport;
+import org.apache.camel.test.testcontainers.Wait;
 import org.junit.Rule;
 import org.junit.rules.TestName;
 import org.testcontainers.containers.GenericContainer;
 
-public class ConsulTestSupport extends CamelTestSupport {
+public class ConsulTestSupport extends ContainerAwareTestSupport {
+    public static final String CONTAINER_IMAGE = "consul:1.0.0";
+    public static final String CONTAINER_NAME = "consul";
     public static final String KV_PREFIX = "/camel";
 
     @Rule
     public final TestName testName = new TestName();
 
-    @Rule
-    public GenericContainer container = 
ConsulContainerSupport.consulContainer();
-
     @Override
     protected JndiRegistry createRegistry() throws Exception {
         JndiRegistry registry = super.createRegistry();
@@ -78,6 +77,32 @@ protected String generateKey() {
     }
 
     protected String consulUrl() {
-        return ConsulContainerSupport.consulUrl(container);
+        return String.format(
+            "http://%s:%d";,
+            getContainerHost(CONTAINER_NAME),
+            getContainerPort(CONTAINER_NAME, Consul.DEFAULT_HTTP_PORT)
+        );
+    }
+
+    @Override
+    protected GenericContainer<?> createContainer() {
+        return consulContainer();
+    }
+
+    public static GenericContainer consulContainer() {
+        return new GenericContainer(CONTAINER_IMAGE)
+            .withNetworkAliases(CONTAINER_NAME)
+            .withExposedPorts(Consul.DEFAULT_HTTP_PORT)
+            .waitingFor(Wait.forLogMessageContaining("Synced node info", 1))
+            .withCommand(
+                "agent",
+                "-dev",
+                "-server",
+                "-bootstrap",
+                "-client",
+                "0.0.0.0",
+                "-log-level",
+                "trace"
+            );
     }
 }
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulDefaultServiceCallRouteTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulDefaultServiceCallRouteTest.java
index aee8c8df7f7..57ec7dfed2d 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulDefaultServiceCallRouteTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulDefaultServiceCallRouteTest.java
@@ -25,7 +25,7 @@
 import com.orbitz.consul.model.agent.Registration;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
+import org.apache.camel.component.consul.ConsulTestSupport;
 import org.junit.Test;
 
 public class ConsulDefaultServiceCallRouteTest extends ConsulTestSupport {
@@ -43,6 +43,8 @@
 
     @Override
     protected void doPreSetup() throws Exception {
+        super.doPreSetup();
+
         client = getConsul().agentClient();
 
         registrations = new ArrayList<>(SERVICE_COUNT);
@@ -64,8 +66,8 @@ protected void doPreSetup() throws Exception {
     }
 
     @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
+    public void doPostTearDown() throws Exception {
+        super.doPostTearDown();
 
         registrations.forEach(r -> client.deregister(r.getId()));
     }
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulRibbonServiceCallRouteTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulRibbonServiceCallRouteTest.java
index f530e314d8a..0359ae5351d 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulRibbonServiceCallRouteTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulRibbonServiceCallRouteTest.java
@@ -25,7 +25,7 @@
 import com.orbitz.consul.model.agent.Registration;
 import org.apache.camel.RoutesBuilder;
 import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
+import org.apache.camel.component.consul.ConsulTestSupport;
 import org.junit.Test;
 
 public class ConsulRibbonServiceCallRouteTest extends ConsulTestSupport {
@@ -43,6 +43,8 @@
 
     @Override
     protected void doPreSetup() throws Exception {
+        super.doPreSetup();
+
         client = getConsul().agentClient();
 
         registrations = new ArrayList<>(SERVICE_COUNT);
@@ -64,10 +66,9 @@ protected void doPreSetup() throws Exception {
     }
 
     @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
-
-
+    public void doPostTearDown() throws Exception {
+        super.doPostTearDown();
+        
         registrations.forEach(r -> client.deregister(r.getId()));
     }
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceCallWithRegistrationTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceCallWithRegistrationTest.java
index 1141c277332..ded452e3cbf 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceCallWithRegistrationTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceCallWithRegistrationTest.java
@@ -22,7 +22,7 @@
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelExecutionException;
 import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
+import org.apache.camel.component.consul.ConsulTestSupport;
 import org.apache.camel.impl.cloud.ServiceRegistrationRoutePolicy;
 import org.junit.Test;
 import org.springframework.util.SocketUtils;
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceDiscoveryTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceDiscoveryTest.java
index 5f3a62d5f3e..7e7f4316489 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceDiscoveryTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceDiscoveryTest.java
@@ -27,7 +27,7 @@
 import org.apache.camel.cloud.ServiceDefinition;
 import org.apache.camel.cloud.ServiceDiscovery;
 import org.apache.camel.component.consul.ConsulConfiguration;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
+import org.apache.camel.component.consul.ConsulTestSupport;
 import org.junit.Test;
 import org.springframework.util.SocketUtils;
 
@@ -42,6 +42,8 @@ public boolean isUseRouteBuilder() {
 
     @Override
     protected void doPreSetup() throws Exception {
+        super.doPreSetup();
+
         client = getConsul().agentClient();
         registrations = new ArrayList<>(3);
 
@@ -72,8 +74,8 @@ protected void doPreSetup() throws Exception {
     }
 
     @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
+    public void doPostTearDown() throws Exception {
+        super.doPostTearDown();
 
         registrations.forEach(r -> client.deregister(r.getId()));
     }
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceRegistrationTestBase.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceRegistrationTestBase.java
index 69a4e1efec8..4f44a369d2f 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceRegistrationTestBase.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceRegistrationTestBase.java
@@ -27,7 +27,7 @@
 import com.orbitz.consul.model.health.ServiceHealth;
 import org.apache.camel.CamelContext;
 import org.apache.camel.cloud.ServiceDefinition;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
+import org.apache.camel.component.consul.ConsulTestSupport;
 import org.junit.Test;
 import org.springframework.util.SocketUtils;
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceRegistryTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceRegistryTest.java
index 5c5ace32ed6..227a1d3e972 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceRegistryTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/ConsulServiceRegistryTest.java
@@ -22,7 +22,7 @@
 import com.orbitz.consul.HealthClient;
 import com.orbitz.consul.model.catalog.CatalogService;
 import com.orbitz.consul.model.health.ServiceHealth;
-import org.apache.camel.component.consul.support.ConsulTestSupport;
+import org.apache.camel.component.consul.ConsulTestSupport;
 import org.apache.camel.impl.cloud.DefaultServiceDefinition;
 import org.junit.Test;
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/SpringConsulServiceCallRouteTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/SpringConsulServiceCallRouteTest.java
index 471a9fa1b2b..5fc670d45be 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/SpringConsulServiceCallRouteTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cloud/SpringConsulServiceCallRouteTest.java
@@ -20,7 +20,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Properties;
 
 import com.orbitz.consul.AgentClient;
 import com.orbitz.consul.Consul;
@@ -29,21 +28,16 @@
 import org.apache.camel.Navigate;
 import org.apache.camel.Processor;
 import org.apache.camel.Route;
-import org.apache.camel.component.consul.support.ConsulContainerSupport;
+import org.apache.camel.component.consul.ConsulTestSupport;
 import org.apache.camel.impl.cloud.DefaultServiceCallProcessor;
 import org.apache.camel.processor.ChoiceProcessor;
 import org.apache.camel.processor.FilterProcessor;
-import org.apache.camel.test.spring.CamelSpringTestSupport;
+import 
org.apache.camel.test.testcontainers.spring.ContainerAwareSpringTestSupport;
 import org.junit.Assert;
-import org.junit.Rule;
 import org.junit.Test;
 import org.testcontainers.containers.GenericContainer;
 
-public abstract class SpringConsulServiceCallRouteTest extends 
CamelSpringTestSupport {
-
-    @Rule
-    public GenericContainer container = 
ConsulContainerSupport.consulContainer();
-
+public abstract class SpringConsulServiceCallRouteTest extends 
ContainerAwareSpringTestSupport {
     private AgentClient client;
     private List<Registration> registrations;
 
@@ -51,18 +45,12 @@
     // Setup / tear down
     // 
*************************************************************************
 
-    @Override
-    protected Properties useOverridePropertiesWithPropertiesComponent() {
-        Properties properties = new Properties();
-        properties.put("consul.url", 
ConsulContainerSupport.consulUrl(container));
-
-        return properties;
-    }
-
     @Override
     public void doPreSetup() throws Exception {
+        super.doPreSetup();
+
         this.client = Consul.builder()
-            .withUrl(ConsulContainerSupport.consulUrl(container))
+            .withUrl(consulUrl())
             .build()
             .agentClient();
 
@@ -106,13 +94,15 @@ public void doPreSetup() throws Exception {
         );
 
         this.registrations.forEach(client::register);
-        super.doPreSetup();
     }
 
     @Override
-    public void tearDown() throws Exception {
-        super.tearDown();
-        registrations.forEach(r -> client.deregister(r.getId()));
+    public void doPostTearDown() throws Exception {
+        super.doPostTearDown();
+
+        if (client != null) {
+            registrations.forEach(r -> client.deregister(r.getId()));
+        }
     }
 
     // 
*************************************************************************
@@ -162,4 +152,17 @@ public void testServiceCall() throws Exception {
 
         return processors;
     }
+
+    @Override
+    protected GenericContainer<?> createContainer() {
+        return ConsulTestSupport.consulContainer();
+    }
+
+    protected String consulUrl() {
+        return String.format(
+            "http://%s:%d";,
+            getContainerHost(ConsulTestSupport.CONTAINER_NAME),
+            getContainerPort(ConsulTestSupport.CONTAINER_NAME, 
Consul.DEFAULT_HTTP_PORT)
+        );
+    }
 }
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cluster/ConsulClusteredRoutePolicyFactoryTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cluster/ConsulClusteredRoutePolicyFactoryTest.java
index 43ced2d2a89..f305d81d3dc 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cluster/ConsulClusteredRoutePolicyFactoryTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cluster/ConsulClusteredRoutePolicyFactoryTest.java
@@ -26,8 +26,9 @@
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
+import com.orbitz.consul.Consul;
 import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.component.consul.support.ConsulContainerSupport;
+import org.apache.camel.component.consul.ConsulTestSupport;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.impl.cluster.ClusteredRoutePolicyFactory;
 import org.junit.Assert;
@@ -40,7 +41,7 @@
 public class ConsulClusteredRoutePolicyFactoryTest {
 
     @ClassRule
-    public static GenericContainer container = 
ConsulContainerSupport.consulContainer();
+    public static GenericContainer container = 
ConsulTestSupport.consulContainer();
 
     private static final Logger LOGGER = 
LoggerFactory.getLogger(ConsulClusteredRoutePolicyFactoryTest.class);
     private static final List<String> CLIENTS = IntStream.range(0, 
3).mapToObj(Integer::toString).collect(Collectors.toList());
@@ -76,7 +77,7 @@ private static void run(String id) {
 
             ConsulClusterService service = new ConsulClusterService();
             service.setId("node-" + id);
-            service.setUrl(ConsulContainerSupport.consulUrl(container));
+            service.setUrl(String.format("http://%s:%d";, 
container.getContainerIpAddress(), 
container.getMappedPort(Consul.DEFAULT_HTTP_PORT)));
 
             LOGGER.info("Consul URL {}", service.getUrl());
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cluster/ConsulClusteredRoutePolicyTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cluster/ConsulClusteredRoutePolicyTest.java
index c2c3b164aee..9502528cac4 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cluster/ConsulClusteredRoutePolicyTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cluster/ConsulClusteredRoutePolicyTest.java
@@ -26,8 +26,9 @@
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
+import com.orbitz.consul.Consul;
 import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.component.consul.support.ConsulContainerSupport;
+import org.apache.camel.component.consul.ConsulTestSupport;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.apache.camel.impl.cluster.ClusteredRoutePolicy;
 import org.junit.Assert;
@@ -40,7 +41,7 @@
 public class ConsulClusteredRoutePolicyTest {
 
     @ClassRule
-    public static GenericContainer container = 
ConsulContainerSupport.consulContainer();
+    public static GenericContainer container = 
ConsulTestSupport.consulContainer();
 
     private static final Logger LOGGER = 
LoggerFactory.getLogger(ConsulClusteredRoutePolicyTest.class);
     private static final List<String> CLIENTS = IntStream.range(0, 
3).mapToObj(Integer::toString).collect(Collectors.toList());
@@ -76,7 +77,7 @@ private static void run(String id) {
 
             ConsulClusterService service = new ConsulClusterService();
             service.setId("node-" + id);
-            service.setUrl(ConsulContainerSupport.consulUrl(container));
+            service.setUrl(String.format("http://%s:%d";, 
container.getContainerIpAddress(), 
container.getMappedPort(Consul.DEFAULT_HTTP_PORT)));
 
             LOGGER.info("Consul URL {}", service.getUrl());
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cluster/ConsulMasterTest.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cluster/ConsulMasterTest.java
index 9a88469956f..bc76b886e2c 100644
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/cluster/ConsulMasterTest.java
+++ 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/cluster/ConsulMasterTest.java
@@ -26,8 +26,9 @@
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
+import com.orbitz.consul.Consul;
 import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.component.consul.support.ConsulContainerSupport;
+import org.apache.camel.component.consul.ConsulTestSupport;
 import org.apache.camel.impl.DefaultCamelContext;
 import org.junit.Assert;
 import org.junit.ClassRule;
@@ -39,7 +40,7 @@
 public class ConsulMasterTest {
 
     @ClassRule
-    public static GenericContainer container = 
ConsulContainerSupport.consulContainer();
+    public static GenericContainer container = 
ConsulTestSupport.consulContainer();
 
     private static final Logger LOGGER = 
LoggerFactory.getLogger(ConsulMasterTest.class);
     private static final List<String> CLIENTS = IntStream.range(0, 
3).mapToObj(Integer::toString).collect(Collectors.toList());
@@ -75,7 +76,7 @@ private static void run(String id) {
 
             ConsulClusterService service = new ConsulClusterService();
             service.setId("node-" + id);
-            service.setUrl(ConsulContainerSupport.consulUrl(container));
+            service.setUrl(String.format("http://%s:%d";, 
container.getContainerIpAddress(), 
container.getMappedPort(Consul.DEFAULT_HTTP_PORT)));
 
             LOGGER.info("Consul URL {}", service.getUrl());
 
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/support/ConsulContainerLogger.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/support/ConsulContainerLogger.java
deleted file mode 100644
index 3f80181aad5..00000000000
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/support/ConsulContainerLogger.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.component.consul.support;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testcontainers.containers.output.Slf4jLogConsumer;
-
-public final class ConsulContainerLogger extends Slf4jLogConsumer {
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(ConsulContainerLogger.class);
-
-    public ConsulContainerLogger() {
-        super(LOGGER);
-
-        withPrefix("consul");
-    }
-}
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/support/ConsulContainerSupport.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/support/ConsulContainerSupport.java
deleted file mode 100644
index 0602f444ebc..00000000000
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/support/ConsulContainerSupport.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.component.consul.support;
-
-import com.orbitz.consul.Consul;
-import org.testcontainers.containers.GenericContainer;
-
-public final class ConsulContainerSupport {
-    private ConsulContainerSupport() {
-    }
-
-    public static GenericContainer consulContainer() {
-        return new GenericContainer("consul:1.0.0")
-            .withExposedPorts(Consul.DEFAULT_HTTP_PORT)
-            .waitingFor(new ConsulContainerWaitStrategy())
-            .withLogConsumer(new ConsulContainerLogger())
-            .withCommand(
-                "agent",
-                "-dev",
-                "-server",
-                "-bootstrap",
-                "-client",
-                "0.0.0.0",
-                "-log-level",
-                "trace"
-            );
-    }
-
-    public static  String consulUrl(GenericContainer container) {
-        return String.format("http://%s:%d";, 
container.getContainerIpAddress(), 
container.getMappedPort(Consul.DEFAULT_HTTP_PORT));
-    }
-}
diff --git 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/support/ConsulContainerWaitStrategy.java
 
b/components/camel-consul/src/test/java/org/apache/camel/component/consul/support/ConsulContainerWaitStrategy.java
deleted file mode 100644
index 8742f3e3b29..00000000000
--- 
a/components/camel-consul/src/test/java/org/apache/camel/component/consul/support/ConsulContainerWaitStrategy.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.component.consul.support;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import com.github.dockerjava.api.DockerClient;
-import org.testcontainers.DockerClientFactory;
-import org.testcontainers.containers.ContainerLaunchException;
-import org.testcontainers.containers.output.WaitingConsumer;
-import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy;
-import org.testcontainers.utility.LogUtils;
-
-public final class ConsulContainerWaitStrategy extends AbstractWaitStrategy {
-    @Override
-    protected void waitUntilReady() {
-        final DockerClient client = DockerClientFactory.instance().client();
-        final WaitingConsumer waitingConsumer = new WaitingConsumer();
-
-        LogUtils.followOutput(client, waitStrategyTarget.getContainerId(), 
waitingConsumer);
-
-        try {
-            waitingConsumer.waitUntil(
-                f -> f.getUtf8String().contains("Synced node info"),
-                startupTimeout.getSeconds(),
-                TimeUnit.SECONDS,
-                1
-            );
-        } catch (TimeoutException e) {
-            throw new ContainerLaunchException("Timed out");
-        }
-    }
-}
\ No newline at end of file
diff --git 
a/components/camel-consul/src/test/resources/org/apache/camel/component/consul/cloud/SpringConsulDefaultServiceCallRouteTest.xml
 
b/components/camel-consul/src/test/resources/org/apache/camel/component/consul/cloud/SpringConsulDefaultServiceCallRouteTest.xml
index 607d6bdd0fd..18f5f9e3f85 100644
--- 
a/components/camel-consul/src/test/resources/org/apache/camel/component/consul/cloud/SpringConsulDefaultServiceCallRouteTest.xml
+++ 
b/components/camel-consul/src/test/resources/org/apache/camel/component/consul/cloud/SpringConsulDefaultServiceCallRouteTest.xml
@@ -33,7 +33,7 @@
 
     <defaultServiceCallConfiguration id="default" component="jetty">
       <!-- service discovery -->
-      <consulServiceDiscovery url="{{consul.url}}"/>
+      <consulServiceDiscovery 
url="http://{{container:host:consul}}:{{container:port:8500@consul}}"/>
 
       <!-- service filter -->
       <blacklistServiceFilter>
diff --git 
a/components/camel-consul/src/test/resources/org/apache/camel/component/consul/cloud/SpringConsulExpressionServiceCallRouteTest.xml
 
b/components/camel-consul/src/test/resources/org/apache/camel/component/consul/cloud/SpringConsulExpressionServiceCallRouteTest.xml
index b55331404f8..e7732b0c3b9 100644
--- 
a/components/camel-consul/src/test/resources/org/apache/camel/component/consul/cloud/SpringConsulExpressionServiceCallRouteTest.xml
+++ 
b/components/camel-consul/src/test/resources/org/apache/camel/component/consul/cloud/SpringConsulExpressionServiceCallRouteTest.xml
@@ -33,7 +33,7 @@
 
     <defaultServiceCallConfiguration id="default" component="jetty">
       <!-- service discovery -->
-      <consulServiceDiscovery url="{{consul.url}}"/>
+      <consulServiceDiscovery 
url="http://{{container:host:consul}}:{{container:port:8500@consul}}"/>
 
       <!-- service filter -->
       <blacklistServiceFilter>
diff --git 
a/components/camel-consul/src/test/resources/org/apache/camel/component/consul/cloud/SpringConsulRibbonServiceCallRouteTest.xml
 
b/components/camel-consul/src/test/resources/org/apache/camel/component/consul/cloud/SpringConsulRibbonServiceCallRouteTest.xml
index 78bb6295105..f7c4105408d 100644
--- 
a/components/camel-consul/src/test/resources/org/apache/camel/component/consul/cloud/SpringConsulRibbonServiceCallRouteTest.xml
+++ 
b/components/camel-consul/src/test/resources/org/apache/camel/component/consul/cloud/SpringConsulRibbonServiceCallRouteTest.xml
@@ -33,7 +33,7 @@
 
     <defaultServiceCallConfiguration id="default" component="jetty">
       <!-- service discovery -->
-      <consulServiceDiscovery url="{{consul.url}}"/>
+      <consulServiceDiscovery 
url="http://{{container:host:consul}}:{{container:port:8500@consul}}"/>
 
       <!-- service filter -->
       <blacklistServiceFilter>
diff --git 
a/components/camel-google-drive/src/test/java/org/apache/camel/component/google/drive/AbstractGoogleDriveTestSupport.java
 
b/components/camel-google-drive/src/test/java/org/apache/camel/component/google/drive/AbstractGoogleDriveTestSupport.java
index b0b4461a900..2ca2c4cf53d 100644
--- 
a/components/camel-google-drive/src/test/java/org/apache/camel/component/google/drive/AbstractGoogleDriveTestSupport.java
+++ 
b/components/camel-google-drive/src/test/java/org/apache/camel/component/google/drive/AbstractGoogleDriveTestSupport.java
@@ -126,11 +126,6 @@ protected CamelContext createCamelContext() throws 
Exception {
         return context;
     }
 
-    @AfterClass
-    public static void tearDownAfterClass() throws Exception {
-        CamelTestSupport.tearDownAfterClass();
-    }
-
     @Override
     public boolean isCreateCamelContextPerClass() {
         // only create the context once for this class
diff --git 
a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/HttpProxyIntegrationTest.java
 
b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/HttpProxyIntegrationTest.java
index 9caccce94de..0effb786c16 100644
--- 
a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/HttpProxyIntegrationTest.java
+++ 
b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/HttpProxyIntegrationTest.java
@@ -150,9 +150,8 @@ protected void createComponent() throws Exception {
         configurationMethod.accept(salesforce);
     }
 
-    @AfterClass
-    public static void tearDownAfterClass() throws Exception {
-        CamelTestSupport.tearDownAfterClass();
+    @Override
+    public void cleanupResources() throws Exception {
         // stop the proxy server after component
         LOG.info("Stopping proxy server...");
         server.stop();
diff --git a/components/camel-spring-cloud-consul/pom.xml 
b/components/camel-spring-cloud-consul/pom.xml
index 76742302156..316628bb4f2 100644
--- a/components/camel-spring-cloud-consul/pom.xml
+++ b/components/camel-spring-cloud-consul/pom.xml
@@ -94,7 +94,7 @@
     <!-- Testing dependencies -->
     <dependency>
       <groupId>org.apache.camel</groupId>
-      <artifactId>camel-test</artifactId>
+      <artifactId>camel-testcontainers</artifactId>
       <scope>test</scope>
     </dependency>
     <dependency>
@@ -135,14 +135,6 @@
       <version>${spring-boot-version}</version>
       <scope>test</scope>
     </dependency>
-
-    <!-- testing (docker) -->
-    <dependency>
-      <groupId>org.testcontainers</groupId>
-      <artifactId>testcontainers</artifactId>
-      <version>${testcontainers-version}</version>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
 
   <build>
diff --git 
a/components/camel-spring-cloud-consul/src/test/java/org/apache/camel/spring/cloud/consul/CamelCloudConsulServiceRegistryTest.java
 
b/components/camel-spring-cloud-consul/src/test/java/org/apache/camel/spring/cloud/consul/CamelCloudConsulServiceRegistryTest.java
index e1a9e95d3d1..d5cb60eb662 100644
--- 
a/components/camel-spring-cloud-consul/src/test/java/org/apache/camel/spring/cloud/consul/CamelCloudConsulServiceRegistryTest.java
+++ 
b/components/camel-spring-cloud-consul/src/test/java/org/apache/camel/spring/cloud/consul/CamelCloudConsulServiceRegistryTest.java
@@ -24,9 +24,11 @@
 import com.ecwid.consul.v1.catalog.model.CatalogService;
 import org.apache.camel.cloud.ServiceRegistry;
 import org.apache.camel.impl.cloud.DefaultServiceDefinition;
-import org.apache.camel.spring.cloud.consul.support.ConsulContainerSupport;
+import org.apache.camel.test.testcontainers.Wait;
 import org.junit.Rule;
 import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.boot.WebApplicationType;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.builder.SpringApplicationBuilder;
@@ -34,17 +36,32 @@
 import org.springframework.context.annotation.Configuration;
 import org.springframework.util.SocketUtils;
 import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class CamelCloudConsulServiceRegistryTest {
-    protected static final String SERVICE_ID = UUID.randomUUID().toString();
-    protected static final String SERVICE_NAME = "my-service";
-    protected static final String SERVICE_HOST = "localhost";
-    protected static final int SERVICE_PORT = 
SocketUtils.findAvailableTcpPort();
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(CamelCloudConsulServiceRegistryTest.class);
+    private static final String SERVICE_ID = UUID.randomUUID().toString();
+    private static final String SERVICE_NAME = "my-service";
+    private static final String SERVICE_HOST = "localhost";
+    private static final int SERVICE_PORT = SocketUtils.findAvailableTcpPort();
 
     @Rule
-    public GenericContainer container = 
ConsulContainerSupport.consulContainer();
+    public GenericContainer container =  new GenericContainer("consul:1.0.0")
+        .withExposedPorts(8500)
+        .waitingFor(Wait.forLogMessageContaining("Synced node info", 1))
+        .withLogConsumer(new Slf4jLogConsumer(LOGGER).withPrefix("consul"))
+        .withCommand(
+            "agent",
+            "-dev",
+            "-server",
+            "-bootstrap",
+            "-client",
+            "0.0.0.0",
+            "-log-level",
+            "trace"
+        );
 
     @Test
     public void testServiceRegistry() throws Exception {
diff --git 
a/components/camel-spring-cloud-consul/src/test/java/org/apache/camel/spring/cloud/consul/support/ConsulContainerLogger.java
 
b/components/camel-spring-cloud-consul/src/test/java/org/apache/camel/spring/cloud/consul/support/ConsulContainerLogger.java
deleted file mode 100644
index 59e5bf288f4..00000000000
--- 
a/components/camel-spring-cloud-consul/src/test/java/org/apache/camel/spring/cloud/consul/support/ConsulContainerLogger.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.spring.cloud.consul.support;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testcontainers.containers.output.Slf4jLogConsumer;
-
-public final class ConsulContainerLogger extends Slf4jLogConsumer {
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(ConsulContainerLogger.class);
-
-    public ConsulContainerLogger() {
-        super(LOGGER);
-
-        withPrefix("consul");
-    }
-}
diff --git 
a/components/camel-spring-cloud-consul/src/test/java/org/apache/camel/spring/cloud/consul/support/ConsulContainerWaitStrategy.java
 
b/components/camel-spring-cloud-consul/src/test/java/org/apache/camel/spring/cloud/consul/support/ConsulContainerWaitStrategy.java
deleted file mode 100644
index 5133d3c6e1d..00000000000
--- 
a/components/camel-spring-cloud-consul/src/test/java/org/apache/camel/spring/cloud/consul/support/ConsulContainerWaitStrategy.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.spring.cloud.consul.support;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import com.github.dockerjava.api.DockerClient;
-import org.testcontainers.DockerClientFactory;
-import org.testcontainers.containers.ContainerLaunchException;
-import org.testcontainers.containers.output.WaitingConsumer;
-import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy;
-import org.testcontainers.utility.LogUtils;
-
-public final class ConsulContainerWaitStrategy extends AbstractWaitStrategy {
-    @Override
-    protected void waitUntilReady() {
-        final DockerClient client = DockerClientFactory.instance().client();
-        final WaitingConsumer waitingConsumer = new WaitingConsumer();
-
-        LogUtils.followOutput(client, waitStrategyTarget.getContainerId(), 
waitingConsumer);
-
-        try {
-            waitingConsumer.waitUntil(
-                f -> f.getUtf8String().contains("Synced node info"),
-                startupTimeout.getSeconds(),
-                TimeUnit.SECONDS,
-                1
-            );
-        } catch (TimeoutException e) {
-            throw new ContainerLaunchException("Timed out");
-        }
-    }
-}
\ No newline at end of file
diff --git a/components/camel-spring-cloud-zookeeper/pom.xml 
b/components/camel-spring-cloud-zookeeper/pom.xml
index 563bd96c0ac..2c1a9ddce9f 100644
--- a/components/camel-spring-cloud-zookeeper/pom.xml
+++ b/components/camel-spring-cloud-zookeeper/pom.xml
@@ -135,14 +135,6 @@
       <version>${spring-boot-version}</version>
       <scope>test</scope>
     </dependency>
-
-    <!-- testing (docker) -->
-    <dependency>
-      <groupId>org.testcontainers</groupId>
-      <artifactId>testcontainers</artifactId>
-      <version>${testcontainers-version}</version>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
 
   <build>
diff --git 
a/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintTestSupport.java
 
b/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintTestSupport.java
index e3c7fd31b5b..56d6ae20f35 100644
--- 
a/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintTestSupport.java
+++ 
b/components/camel-test-blueprint/src/main/java/org/apache/camel/test/blueprint/CamelBlueprintTestSupport.java
@@ -47,7 +47,6 @@
 import org.apache.camel.test.junit4.CamelTestSupport;
 import org.apache.camel.util.KeyValueHolder;
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Before;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
@@ -384,11 +383,8 @@ protected String 
setConfigAdminInitialConfiguration(Properties props) {
     public void tearDown() throws Exception {
         System.clearProperty("skipStartingCamelContext");
         System.clearProperty("registerBlueprintCamelContextEager");
+
         super.tearDown();
-        if (isCreateCamelContextPerClass()) {
-            // we tear down in after class
-            return;
-        }
 
         // unregister services
         if (bundleContext != null) {
@@ -396,16 +392,17 @@ public void tearDown() throws Exception {
                 bundleContext.ungetService(reg.getReference());
             }
         }
+
         CamelBlueprintHelper.disposeBundleContext(bundleContext);
     }
-    
-    @AfterClass
-    public static void tearDownAfterClass() throws Exception {
+
+    @Override
+    public void cleanupResources() throws Exception {
         if (threadLocalBundleContext.get() != null) {
             
CamelBlueprintHelper.disposeBundleContext(threadLocalBundleContext.get());
             threadLocalBundleContext.remove();
         }
-        CamelTestSupport.tearDownAfterClass();
+        super.cleanupResources();
     }
 
     /**
diff --git 
a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestSupport.java
 
b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestSupport.java
index 41e404e80bf..121176cf47c 100644
--- 
a/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestSupport.java
+++ 
b/components/camel-test-spring/src/main/java/org/apache/camel/test/spring/CamelSpringTestSupport.java
@@ -28,7 +28,6 @@
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.ObjectHelper;
 import org.junit.After;
-import org.junit.AfterClass;
 import org.springframework.beans.factory.support.RootBeanDefinition;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.AbstractApplicationContext;
@@ -121,8 +120,10 @@ public void tearDown() throws Exception {
         }
     }
 
-    @AfterClass
-    public static void tearSpringDownAfterClass() throws Exception {
+    @Override
+    public void doPostTearDown() throws Exception {
+        super.doPostTearDown();
+
         if (threadAppContext.get() != null) {
             IOHelper.close(threadAppContext.get());
             threadAppContext.remove();
diff --git 
a/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTestSupport.java
 
b/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTestSupport.java
index 3b9df0284ff..c77687c6f88 100644
--- 
a/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTestSupport.java
+++ 
b/components/camel-test/src/main/java/org/apache/camel/test/junit4/CamelTestSupport.java
@@ -32,6 +32,7 @@
 import java.util.Properties;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 import javax.management.AttributeNotFoundException;
 import javax.management.InstanceNotFoundException;
@@ -79,7 +80,6 @@
 import org.apache.camel.util.StopWatch;
 import org.apache.camel.util.TimeUtils;
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.Rule;
 import org.slf4j.Logger;
@@ -98,7 +98,6 @@
     public static final String ROUTE_COVERAGE_ENABLED = 
"CamelTestRouteCoverage";
 
     private static final Logger LOG = 
LoggerFactory.getLogger(CamelTestSupport.class);
-    private static final ThreadLocal<Boolean> INIT = new ThreadLocal<>();
     private static ThreadLocal<ModelCamelContext> threadCamelContext = new 
ThreadLocal<>();
     private static ThreadLocal<ProducerTemplate> threadTemplate = new 
ThreadLocal<>();
     private static ThreadLocal<FluentProducerTemplate> threadFluentTemplate = 
new ThreadLocal<>();
@@ -114,6 +113,7 @@
     private final DebugBreakpoint breakpoint = new DebugBreakpoint();
     private final StopWatch watch = new StopWatch();
     private final Map<String, String> fromEndpoints = new HashMap<>();
+    private final AtomicInteger tests = new AtomicInteger(0);
     private CamelTestWatcher camelTestWatcher = new CamelTestWatcher();
 
     /**
@@ -253,21 +253,29 @@ public void setUp() throws Exception {
         
log.info("********************************************************************************");
 
         if (isCreateCamelContextPerClass()) {
-            // test is per class, so only setup once (the first time)
-            boolean first = INIT.get() == null;
-            if (first) {
-                doSpringBootCheck();
-                doPreSetup();
-                doSetUp();
-                doPostSetup();
-            } else {
-                // and in between tests we must do IoC and reset mocks
-                postProcessTest();
-                resetMocks();
+            while (true) {
+                int v = tests.get();
+                if (tests.compareAndSet(v, v + 1)) {
+                    if (v == 0) {
+                        // test is per class, so only setup once (the first 
time)
+                        doSpringBootCheck();
+                        setupResources();
+                        doPreSetup();
+                        doSetUp();
+                        doPostSetup();
+                    } else {
+                        // and in between tests we must do IoC and reset mocks
+                        postProcessTest();
+                        resetMocks();
+                    }
+
+                    break;
+                }
             }
         } else {
             // test is per test so always setup
             doSpringBootCheck();
+            setupResources();
             doPreSetup();
             doSetUp();
             doPostSetup();
@@ -389,8 +397,6 @@ private void doSetUp() throws Exception {
         log.debug("Routing Rules are: " + context.getRoutes());
 
         assertValidContext(context);
-
-        INIT.set(true);
     }
 
     private void replaceFromEndpoints() throws Exception {
@@ -446,13 +452,53 @@ public void tearDown() throws Exception {
         
log.info("********************************************************************************");
 
         if (isCreateCamelContextPerClass()) {
-            // we tear down in after class
-            return;
+            while (true) {
+                int v = tests.get();
+                if (v <= 0) {
+                    LOG.warn("Test already teared down");
+                    break;
+                }
+
+                if (tests.compareAndSet(v, v - 1)) {
+                    if (v == 1) {
+                        LOG.debug("tearDown test");
+                        doStopTemplates(threadConsumer.get(), 
threadTemplate.get(), threadFluentTemplate.get());
+                        doStopCamelContext(threadCamelContext.get(), 
threadService.get());
+                        doPostTearDown();
+                        cleanupResources();
+                    }
+
+                    break;
+                }
+            }
+        } else {
+            LOG.debug("tearDown test");
+            doStopTemplates(consumer, template, fluentTemplate);
+            doStopCamelContext(context, camelContextService);
+            doPostTearDown();
+            cleanupResources();
         }
+    }
 
-        LOG.debug("tearDown test");
-        doStopTemplates(consumer, template, fluentTemplate);
-        doStopCamelContext(context, camelContextService);
+    /**
+     * Strategy to perform any post action, after {@link CamelContext} is 
stopped
+     */
+    protected void doPostTearDown() throws Exception {
+        // noop
+    }
+
+    /**
+     * Strategy to perform resources setup, before {@link CamelContext} is 
created
+     */
+    protected void setupResources() throws Exception {
+        // noop
+    }
+
+    /**
+     * Strategy to perform resources cleanup, after {@link CamelContext} is 
stopped
+     */
+    protected void cleanupResources() throws Exception {
+        // noop
     }
 
     /**
@@ -552,14 +598,6 @@ private void logCoverageSummary(ManagedCamelContextMBean 
managedCamelContext) th
         return processorsForRoute;
     }
 
-    @AfterClass
-    public static void tearDownAfterClass() throws Exception {
-        INIT.remove();
-        LOG.debug("tearDownAfterClass test");
-        doStopTemplates(threadConsumer.get(), threadTemplate.get(), 
threadFluentTemplate.get());
-        doStopCamelContext(threadCamelContext.get(), threadService.get());
-    }
-
     /**
      * Gathers test details as xml
      */
diff --git a/components/camel-testcontainers-spring/pom.xml 
b/components/camel-testcontainers-spring/pom.xml
new file mode 100644
index 00000000000..fd71ae8c05a
--- /dev/null
+++ b/components/camel-testcontainers-spring/pom.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>components</artifactId>
+        <version>2.22.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-testcontainers-spring</artifactId>
+    <packaging>jar</packaging>
+    <name>Camel :: Testcontainers :: Spring</name>
+    <description>Camel unit testing with Spring and  
testcontainers</description>
+
+    <properties>
+        <!-- use by camel-catalog -->
+        <firstVersion>2.22.0</firstVersion>
+        <label>testing,java,docker</label>
+
+        
<camel.osgi.export.pkg>org.apache.camel.test.testcontainers.spring.*</camel.osgi.export.pkg>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-spring</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-testcontainers</artifactId>
+        </dependency>
+        <!-- optional dependencies for running tests -->
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>${assertj-version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>jdk9+-build</id>
+            <activation>
+                <jdk>[9,)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <argLine>--add-modules java.xml.bind --add-opens 
java.base/java.lang=ALL-UNNAMED</argLine>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <!-- activate integration test if the docker socket file is accessible 
-->
+        <profile>
+            <id>testcontainers-spring-integration-tests-docker-file</id>
+            <activation>
+                <file>
+                    <exists>/var/run/docker.sock</exists>
+                </file>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                
<visibleassertions.silence>true</visibleassertions.silence>
+                            </systemPropertyVariables>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <!-- activate integration test if the DOCKER_HOST env var is set -->
+        <profile>
+            <id>testcontainers-spring-integration-tests-docker-env</id>
+            <activation>
+                <property>
+                    <name>env.DOCKER_HOST</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                
<visibleassertions.silence>true</visibleassertions.silence>
+                            </systemPropertyVariables>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+    </profiles>
+</project>
diff --git 
a/components/camel-testcontainers-spring/src/main/java/org/apache/camel/test/testcontainers/spring/ContainerAwareSpringTestSupport.java
 
b/components/camel-testcontainers-spring/src/main/java/org/apache/camel/test/testcontainers/spring/ContainerAwareSpringTestSupport.java
new file mode 100644
index 00000000000..d5462cd5629
--- /dev/null
+++ 
b/components/camel-testcontainers-spring/src/main/java/org/apache/camel/test/testcontainers/spring/ContainerAwareSpringTestSupport.java
@@ -0,0 +1,112 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.testcontainers.spring;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.properties.PropertiesComponent;
+import org.apache.camel.test.spring.CamelSpringTestSupport;
+import org.apache.camel.test.testcontainers.ContainerPropertiesFunction;
+import org.apache.camel.test.testcontainers.Containers;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.Network;
+
+public abstract class ContainerAwareSpringTestSupport extends 
CamelSpringTestSupport {
+    private List<GenericContainer<?>> containers = new 
CopyOnWriteArrayList<>();
+
+    // ******************
+    // Setup
+    // ******************
+
+    @Override
+    protected void setupResources() throws Exception {
+        super.setupResources();
+
+        containers.clear();
+        containers.addAll(createContainers());
+
+        final Network network = containerNetwork();
+        final long timeout = containersStartupTimeout();
+
+        Containers.start(containers, network, timeout);
+    }
+
+    @Override
+    protected void cleanupResources() throws Exception {
+        super.cleanupResources();
+
+        Containers.stop(containers, containerShutdownTimeout());
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        final CamelContext context = super.createCamelContext();
+        final PropertiesComponent pc = context.getComponent("properties", 
PropertiesComponent.class);
+
+        pc.addFunction(new ContainerPropertiesFunction(containers));
+
+        return context;
+    }
+
+    // ******************
+    // Containers set-up
+    // ******************
+
+    protected GenericContainer<?> createContainer() {
+        return null;
+    }
+
+    protected List<GenericContainer<?>> createContainers() {
+        GenericContainer<?> container = createContainer();
+
+        return container == null
+            ? Collections.emptyList()
+            : Collections.singletonList(container);
+    }
+
+    protected long containersStartupTimeout() {
+        return TimeUnit.MINUTES.toSeconds(1);
+    }
+
+    protected long containerShutdownTimeout() {
+        return TimeUnit.MINUTES.toSeconds(1);
+    }
+
+    protected Network containerNetwork() {
+        return null;
+    }
+
+    // ******************
+    // Helpers
+    // ******************
+
+    protected GenericContainer<?> getContainer(String containerName) {
+        return Containers.lookup(containers, containerName);
+    }
+
+    protected String getContainerHost(String containerName) {
+        return getContainer(containerName).getContainerIpAddress();
+    }
+
+    protected int getContainerPort(String containerName, int originalPort) {
+        return getContainer(containerName).getMappedPort(originalPort);
+    }
+}
diff --git 
a/components/camel-testcontainers-spring/src/main/resources/META-INF/LICENSE.txt
 
b/components/camel-testcontainers-spring/src/main/resources/META-INF/LICENSE.txt
new file mode 100644
index 00000000000..6b0b1270ff0
--- /dev/null
+++ 
b/components/camel-testcontainers-spring/src/main/resources/META-INF/LICENSE.txt
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git 
a/components/camel-testcontainers-spring/src/main/resources/META-INF/NOTICE.txt 
b/components/camel-testcontainers-spring/src/main/resources/META-INF/NOTICE.txt
new file mode 100644
index 00000000000..2e215bf2e6b
--- /dev/null
+++ 
b/components/camel-testcontainers-spring/src/main/resources/META-INF/NOTICE.txt
@@ -0,0 +1,11 @@
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Apache Camel distribution.                    ==
+   =========================================================================
+
+   This product includes software developed by
+   The Apache Software Foundation (http://www.apache.org/).
+
+   Please read the different LICENSE files present in the licenses directory of
+   this distribution.
diff --git 
a/components/camel-testcontainers-spring/src/test/java/ContainerAwareSpringTestSupportIT.java
 
b/components/camel-testcontainers-spring/src/test/java/ContainerAwareSpringTestSupportIT.java
new file mode 100644
index 00000000000..956b0003556
--- /dev/null
+++ 
b/components/camel-testcontainers-spring/src/test/java/ContainerAwareSpringTestSupportIT.java
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.camel.test.testcontainers.Wait;
+import 
org.apache.camel.test.testcontainers.spring.ContainerAwareSpringTestSupport;
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+import org.springframework.context.support.AbstractApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.testcontainers.containers.GenericContainer;
+
+public class ContainerAwareSpringTestSupportIT extends 
ContainerAwareSpringTestSupport {
+    @Override
+    protected AbstractApplicationContext createApplicationContext() {
+        return new 
ClassPathXmlApplicationContext("org/apache/camel/test/testcontainers/spring/ContainerAwareSpringTestSupportTest.xml");
+    }
+
+    @Test
+    public void testPropertyPlaceholders() throws Exception {
+        final GenericContainer<?> container = getContainer("myconsul");
+
+        final String host = 
context.resolvePropertyPlaceholders("{{container:host:myconsul}}");
+        
Assertions.assertThat(host).isEqualTo(container.getContainerIpAddress());
+
+        final String port = 
context.resolvePropertyPlaceholders("{{container:port:8500@myconsul}}");
+        Assertions.assertThat(port).isEqualTo("" + 
container.getMappedPort(8500));
+    }
+
+    @Override
+    protected GenericContainer<?> createContainer() {
+        return new GenericContainer("consul:1.0.0")
+            .withNetworkAliases("myconsul")
+            .withExposedPorts(8500)
+            .waitingFor(Wait.forLogMessageContaining("Synced node info", 1))
+            .withCommand(
+                "agent",
+                "-dev",
+                "-server",
+                "-bootstrap",
+                "-client",
+                "0.0.0.0",
+                "-log-level",
+                "trace"
+            );
+    }
+
+}
diff --git 
a/components/camel-testcontainers-spring/src/test/resources/log4j2.properties 
b/components/camel-testcontainers-spring/src/test/resources/log4j2.properties
new file mode 100644
index 00000000000..6d3bc2f6d96
--- /dev/null
+++ 
b/components/camel-testcontainers-spring/src/test/resources/log4j2.properties
@@ -0,0 +1,30 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements.  See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License.  You may obtain a copy of the License at
+##
+##      http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-testcontainers-spring.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d %-5p %c{1} - %m %n
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = [%30.30t] %-30.30c{1} %-5p %m%n
+logger.spring.name = org.apache.camel.test.testcontainers.spring
+logger.spring.level = DEBUG
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
diff --git 
a/components/camel-testcontainers-spring/src/test/resources/org/apache/camel/test/testcontainers/spring/ContainerAwareSpringTestSupportTest.xml
 
b/components/camel-testcontainers-spring/src/test/resources/org/apache/camel/test/testcontainers/spring/ContainerAwareSpringTestSupportTest.xml
new file mode 100644
index 00000000000..746954c9c17
--- /dev/null
+++ 
b/components/camel-testcontainers-spring/src/test/resources/org/apache/camel/test/testcontainers/spring/ContainerAwareSpringTestSupportTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xmlns:camel="http://camel.apache.org/schema/spring";
+       xsi:schemaLocation="
+               http://camel.apache.org/schema/spring 
http://camel.apache.org/schema/spring/camel-spring.xsd
+               http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd ">
+
+  <camelContext id="camelContext" 
xmlns="http://camel.apache.org/schema/spring";>
+    <route>
+      <from uri="direct:start"/>
+      <to uri="mock:result"/>
+    </route>
+  </camelContext>
+</beans>
\ No newline at end of file
diff --git a/components/camel-testcontainers/pom.xml 
b/components/camel-testcontainers/pom.xml
new file mode 100644
index 00000000000..6a993ceb289
--- /dev/null
+++ b/components/camel-testcontainers/pom.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>components</artifactId>
+        <version>2.22.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-testcontainers</artifactId>
+    <packaging>jar</packaging>
+    <name>Camel :: Testcontainers</name>
+    <description>Camel support for testcontainers</description>
+
+    <properties>
+        <!-- use by camel-catalog -->
+        <firstVersion>2.22.0</firstVersion>
+        <label>testing,java,docker</label>
+
+        
<camel.osgi.export.pkg>org.apache.camel.testcontainers.*</camel.osgi.export.pkg>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers</artifactId>
+            <version>${testcontainers-version}</version>
+        </dependency>
+
+        <!-- optional dependencies for running tests -->
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>${assertj-version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>jdk9+-build</id>
+            <activation>
+                <jdk>[9,)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <argLine>--add-modules java.xml.bind --add-opens 
java.base/java.lang=ALL-UNNAMED</argLine>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <!-- activate integration test if the docker socket file is accessible 
-->
+        <profile>
+            <id>testcontainers-integration-tests-docker-file</id>
+            <activation>
+                <file>
+                    <exists>/var/run/docker.sock</exists>
+                </file>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                
<visibleassertions.silence>true</visibleassertions.silence>
+                            </systemPropertyVariables>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <!-- activate integration test if the DOCKER_HOST env var is set -->
+        <profile>
+            <id>testcontainers-integration-tests-docker-env</id>
+            <activation>
+                <property>
+                    <name>env.DOCKER_HOST</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                
<visibleassertions.silence>true</visibleassertions.silence>
+                            </systemPropertyVariables>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git 
a/components/camel-testcontainers/src/main/java/org/apache/camel/test/testcontainers/ContainerAwareTestSupport.java
 
b/components/camel-testcontainers/src/main/java/org/apache/camel/test/testcontainers/ContainerAwareTestSupport.java
new file mode 100644
index 00000000000..c705b6ec624
--- /dev/null
+++ 
b/components/camel-testcontainers/src/main/java/org/apache/camel/test/testcontainers/ContainerAwareTestSupport.java
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.testcontainers;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.properties.PropertiesComponent;
+import org.apache.camel.test.junit4.CamelTestSupport;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.Network;
+
+public class ContainerAwareTestSupport extends CamelTestSupport {
+    private List<GenericContainer<?>> containers = new 
CopyOnWriteArrayList<>();
+
+    // ******************
+    // Setup
+    // ******************
+
+    @Override
+    protected void setupResources() throws Exception {
+        super.setupResources();
+
+        containers.clear();
+        containers.addAll(createContainers());
+
+        final Network network = containerNetwork();
+        final long timeout = containersStartupTimeout();
+
+        Containers.start(containers, network, timeout);
+    }
+
+    @Override
+    protected void cleanupResources() throws Exception {
+        super.cleanupResources();
+
+        Containers.stop(containers, containerShutdownTimeout());
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        final CamelContext context = super.createCamelContext();
+        final PropertiesComponent pc = context.getComponent("properties", 
PropertiesComponent.class);
+
+        pc.addFunction(new ContainerPropertiesFunction(containers));
+
+        return context;
+    }
+
+    // ******************
+    // Containers set-up
+    // ******************
+
+    protected GenericContainer<?> createContainer() {
+        return null;
+    }
+
+    protected List<GenericContainer<?>> createContainers() {
+        GenericContainer<?> container = createContainer();
+
+        return container == null
+            ? Collections.emptyList()
+            : Collections.singletonList(container);
+    }
+
+    protected long containersStartupTimeout() {
+        return TimeUnit.MINUTES.toSeconds(1);
+    }
+
+    protected long containerShutdownTimeout() {
+        return TimeUnit.MINUTES.toSeconds(1);
+    }
+
+    protected Network containerNetwork() {
+        return null;
+    }
+
+    // ******************
+    // Helpers
+    // ******************
+
+    protected GenericContainer<?> getContainer(String containerName) {
+        return Containers.lookup(containers, containerName);
+    }
+
+    protected String getContainerHost(String containerName) {
+        return getContainer(containerName).getContainerIpAddress();
+    }
+
+    protected int getContainerPort(String containerName, int originalPort) {
+        return getContainer(containerName).getMappedPort(originalPort);
+    }
+}
diff --git 
a/components/camel-testcontainers/src/main/java/org/apache/camel/test/testcontainers/ContainerPropertiesFunction.java
 
b/components/camel-testcontainers/src/main/java/org/apache/camel/test/testcontainers/ContainerPropertiesFunction.java
new file mode 100644
index 00000000000..920037a4f54
--- /dev/null
+++ 
b/components/camel-testcontainers/src/main/java/org/apache/camel/test/testcontainers/ContainerPropertiesFunction.java
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.testcontainers;
+
+import java.util.List;
+
+import org.apache.camel.component.properties.PropertiesFunction;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
+import org.testcontainers.containers.GenericContainer;
+
+public class ContainerPropertiesFunction implements PropertiesFunction {
+    private final List<GenericContainer<?>> containers;
+
+    public ContainerPropertiesFunction(List<GenericContainer<?>> containers) {
+        this.containers = ObjectHelper.notNull(containers, "Containers");
+    }
+
+    @Override
+    public String getName() {
+        return "container";
+    }
+
+    @Override
+    public String apply(String remainder) {
+        final String type = StringHelper.before(remainder, ":");
+        final String query = StringHelper.after(remainder, ":");
+
+        if (ObjectHelper.isEmpty(type)) {
+            throw new IllegalArgumentException("container function syntax is 
container:type:query");
+        }
+
+        if ("host".equalsIgnoreCase(type)) {
+            String name = StringHelper.after(remainder, ":");
+
+            if (ObjectHelper.isEmpty(name)) {
+                throw new IllegalArgumentException("unable to determine 
container name");
+            }
+
+            return Containers.lookup(containers, StringHelper.after(remainder, 
":")).getContainerIpAddress();
+        }
+
+        if ("port".equalsIgnoreCase(type)) {
+            String port = StringHelper.before(query, "@");
+            String name = StringHelper.after(query, "@");
+
+            if (ObjectHelper.isEmpty(port)) {
+                throw new IllegalArgumentException("unable to determine 
original port");
+            }
+
+            if (ObjectHelper.isEmpty(name)) {
+                throw new IllegalArgumentException("unable to determine 
container name");
+            }
+
+            return Integer.toString(
+                Containers.lookup(containers, 
name).getMappedPort(Integer.parseInt(port))
+            );
+        }
+
+        throw new IllegalArgumentException("Unsupported type: " + type);
+    }
+}
diff --git 
a/components/camel-testcontainers/src/main/java/org/apache/camel/test/testcontainers/Containers.java
 
b/components/camel-testcontainers/src/main/java/org/apache/camel/test/testcontainers/Containers.java
new file mode 100644
index 00000000000..8950b7384c9
--- /dev/null
+++ 
b/components/camel-testcontainers/src/main/java/org/apache/camel/test/testcontainers/Containers.java
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.testcontainers;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static java.util.stream.Collectors.joining;
+
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.Network;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+
+public final class Containers {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(Containers.class);
+
+    private Containers() {
+    }
+
+    public static void start(List<GenericContainer<?>> containers, Network 
network, long timeout) throws Exception {
+        final CountDownLatch latch = new CountDownLatch(containers.size());
+
+        for (GenericContainer<?> container: containers) {
+            if (ObjectHelper.isEmpty(container.getNetworkAliases())) {
+                throw new IllegalStateException("Container should have at 
least a network alias");
+            }
+
+            if (network != null) {
+                container.withNetwork(network);
+            }
+
+            // Add custom logger
+            container.withLogConsumer(
+                new 
Slf4jLogConsumer(LOGGER).withPrefix(container.getNetworkAliases().stream().collect(joining(",")))
+            );
+
+            new Thread(() -> {
+                container.start();
+                latch.countDown();
+            }).start();
+        }
+
+        latch.await(timeout, TimeUnit.SECONDS);
+    }
+
+    public static void stop(List<GenericContainer<?>> containers, long 
timeout) throws Exception {
+        final CountDownLatch latch = new CountDownLatch(containers.size());
+
+        for (GenericContainer<?> container: containers) {
+            new Thread(() -> {
+                container.stop();
+                latch.countDown();
+            }).start();
+        }
+
+        latch.await(timeout, TimeUnit.SECONDS);
+    }
+
+    public static GenericContainer<?> lookup(List<GenericContainer<?>> 
containers, String containerName) {
+        for (GenericContainer<?> container: containers) {
+            if (container.getNetworkAliases().contains(containerName)) {
+                return container;
+            }
+        }
+
+        throw new IllegalArgumentException("No container with name " + 
containerName + " found");
+    }
+}
diff --git 
a/components/camel-testcontainers/src/main/java/org/apache/camel/test/testcontainers/Wait.java
 
b/components/camel-testcontainers/src/main/java/org/apache/camel/test/testcontainers/Wait.java
new file mode 100644
index 00000000000..ea712b87c17
--- /dev/null
+++ 
b/components/camel-testcontainers/src/main/java/org/apache/camel/test/testcontainers/Wait.java
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.testcontainers;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Predicate;
+
+import com.github.dockerjava.api.DockerClient;
+import org.testcontainers.DockerClientFactory;
+import org.testcontainers.containers.ContainerLaunchException;
+import org.testcontainers.containers.output.OutputFrame;
+import org.testcontainers.containers.output.WaitingConsumer;
+import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy;
+import org.testcontainers.containers.wait.strategy.WaitStrategy;
+import org.testcontainers.utility.LogUtils;
+
+public class Wait extends org.testcontainers.containers.wait.strategy.Wait {
+    /**
+     * Convenience method to return a WaitStrategy for log messages using a 
predicate.
+     *
+     * @param predicate the predicate to apply to log messages
+     * @param times the number of times the pattern is expected
+     * @return WaitStrategy
+     */
+    public static WaitStrategy forLogPredicate(Predicate<OutputFrame> 
predicate, int times) {
+        return new AbstractWaitStrategy() {
+            @Override
+            protected void waitUntilReady() {
+                final DockerClient client = 
DockerClientFactory.instance().client();
+                final WaitingConsumer waitingConsumer = new WaitingConsumer();
+
+                LogUtils.followOutput(client, 
waitStrategyTarget.getContainerId(), waitingConsumer);
+
+                try {
+                    waitingConsumer.waitUntil(
+                        predicate,
+                        startupTimeout.getSeconds(),
+                        TimeUnit.SECONDS,
+                        times
+                    );
+                } catch (TimeoutException e) {
+                    throw new ContainerLaunchException("Timed out");
+                }
+            }
+        };
+    }
+
+    /**
+     * Convenience method to return a WaitStrategy for log messages.
+     *
+     * @param text the text to find
+     * @param times the number of times the pattern is expected
+     * @return WaitStrategy
+     */
+    public static WaitStrategy forLogMessageContaining(String text, int times) 
{
+        return forLogPredicate(u -> u.getUtf8String().contains(text), times);
+    }
+}
diff --git 
a/components/camel-testcontainers/src/main/resources/META-INF/LICENSE.txt 
b/components/camel-testcontainers/src/main/resources/META-INF/LICENSE.txt
new file mode 100644
index 00000000000..6b0b1270ff0
--- /dev/null
+++ b/components/camel-testcontainers/src/main/resources/META-INF/LICENSE.txt
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git 
a/components/camel-testcontainers/src/main/resources/META-INF/NOTICE.txt 
b/components/camel-testcontainers/src/main/resources/META-INF/NOTICE.txt
new file mode 100644
index 00000000000..2e215bf2e6b
--- /dev/null
+++ b/components/camel-testcontainers/src/main/resources/META-INF/NOTICE.txt
@@ -0,0 +1,11 @@
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Apache Camel distribution.                    ==
+   =========================================================================
+
+   This product includes software developed by
+   The Apache Software Foundation (http://www.apache.org/).
+
+   Please read the different LICENSE files present in the licenses directory of
+   this distribution.
diff --git 
a/components/camel-spring-cloud-consul/src/test/java/org/apache/camel/spring/cloud/consul/support/ConsulContainerSupport.java
 
b/components/camel-testcontainers/src/test/java/org/apache/camel/test/testcontainers/ContainerAwareTestSupportIT.java
similarity index 57%
rename from 
components/camel-spring-cloud-consul/src/test/java/org/apache/camel/spring/cloud/consul/support/ConsulContainerSupport.java
rename to 
components/camel-testcontainers/src/test/java/org/apache/camel/test/testcontainers/ContainerAwareTestSupportIT.java
index 677e582896b..38e8659c7f5 100644
--- 
a/components/camel-spring-cloud-consul/src/test/java/org/apache/camel/spring/cloud/consul/support/ConsulContainerSupport.java
+++ 
b/components/camel-testcontainers/src/test/java/org/apache/camel/test/testcontainers/ContainerAwareTestSupportIT.java
@@ -14,19 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.spring.cloud.consul.support;
+package org.apache.camel.test.testcontainers;
 
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
 import org.testcontainers.containers.GenericContainer;
 
-public final class ConsulContainerSupport {
-    private ConsulContainerSupport() {
+public class ContainerAwareTestSupportIT extends ContainerAwareTestSupport {
+    @Test
+    public void testPropertyPlaceholders() throws Exception {
+        final GenericContainer<?> container = getContainer("myconsul");
+
+        final String host = 
context.resolvePropertyPlaceholders("{{container:host:myconsul}}");
+        
Assertions.assertThat(host).isEqualTo(container.getContainerIpAddress());
+
+        final String port = 
context.resolvePropertyPlaceholders("{{container:port:8500@myconsul}}");
+        Assertions.assertThat(port).isEqualTo("" + 
container.getMappedPort(8500));
     }
 
-    public static GenericContainer consulContainer() {
+    @Override
+    protected GenericContainer<?> createContainer() {
         return new GenericContainer("consul:1.0.0")
+            .withNetworkAliases("myconsul")
             .withExposedPorts(8500)
-            .waitingFor(new ConsulContainerWaitStrategy())
-            .withLogConsumer(new ConsulContainerLogger())
+            .waitingFor(Wait.forLogMessageContaining("Synced node info", 1))
             .withCommand(
                 "agent",
                 "-dev",
@@ -38,4 +49,5 @@ public static GenericContainer consulContainer() {
                 "trace"
             );
     }
+
 }
diff --git 
a/components/camel-testcontainers/src/test/resources/log4j2.properties 
b/components/camel-testcontainers/src/test/resources/log4j2.properties
new file mode 100644
index 00000000000..7db2ad42cf8
--- /dev/null
+++ b/components/camel-testcontainers/src/test/resources/log4j2.properties
@@ -0,0 +1,30 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements.  See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License.  You may obtain a copy of the License at
+##
+##      http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-testcontainers.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d %-5p %c{1} - %m %n
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = [%30.30t] %-30.30c{1} %-5p %m%n
+logger.spring.name = org.apache.camel.test.testcontainers
+logger.spring.level = DEBUG
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
diff --git a/components/pom.xml b/components/pom.xml
index 9e35306d0fb..e88e438e1bb 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -41,6 +41,8 @@
     <module>camel-test-cdi</module>
     <module>camel-test-karaf</module>
     <module>camel-test-spring</module>
+    <module>camel-testcontainers</module>
+    <module>camel-testcontainers-spring</module>
     <module>camel-core-osgi</module>
     <module>camel-core-xml</module>
     <module>camel-blueprint</module>
diff --git a/components/readme.adoc b/components/readme.adoc
index 6ae0f4d72f4..a89bf4b782f 100644
--- a/components/readme.adoc
+++ b/components/readme.adoc
@@ -1075,7 +1075,7 @@ Miscellaneous Components
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
 // others: START
-Number of Miscellaneous Components: 43 in 43 JAR artifacts (13 deprecated)
+Number of Miscellaneous Components: 45 in 45 JAR artifacts (13 deprecated)
 
 [width="100%",cols="4,1,5",options="header"]
 |===
@@ -1159,6 +1159,10 @@ Number of Miscellaneous Components: 43 in 43 JAR 
artifacts (13 deprecated)
 
 | link:camel-test-spring/src/main/docs/test-spring.adoc[Test Spring] 
(camel-test-spring) | 2.10 | Camel unit testing with Spring
 
+| link:camel-testcontainers/src/main/docs/testcontainers.adoc[Testcontainers] 
(camel-testcontainers) | 2.22 | Camel support for testcontainers
+
+| 
link:camel-testcontainers-spring/src/main/docs/testcontainers-spring.adoc[Testcontainers
 Spring] (camel-testcontainers-spring) | 2.22 | Camel unit testing with Spring 
and  testcontainers
+
 | link:camel-testng/src/main/docs/testng.adoc[TestNG] (camel-testng) | 2.8 | 
*deprecated* Camel unit testing with TestNG
 
 | link:camel-urlrewrite/src/main/docs/urlrewrite.adoc[URLRewrite] 
(camel-urlrewrite) | 2.11 | *deprecated* URL rewrite support for HTTP components
diff --git a/docs/user-manual/en/SUMMARY.md b/docs/user-manual/en/SUMMARY.md
index f8ae49fd5ff..a5af9556b8e 100644
--- a/docs/user-manual/en/SUMMARY.md
+++ b/docs/user-manual/en/SUMMARY.md
@@ -438,6 +438,8 @@
        * [Test CDI](test-cdi.adoc)
        * [Test Karaf](test-karaf.adoc)
        * [Test Spring](test-spring.adoc)
+       * [Testcontainers](testcontainers.adoc)
+       * [Testcontainers Spring](testcontainers-spring.adoc)
        * [TestNG](testng.adoc)
        * [URLRewrite](urlrewrite.adoc)
        * [XRay](aws-xray.adoc)
diff --git a/parent/pom.xml b/parent/pom.xml
index 0a0e1eabf86..26357d4ff02 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -2144,6 +2144,16 @@
         <artifactId>camel-test-spring</artifactId>
         <version>${project.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-testcontainers</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-testcontainers-spring</artifactId>
+        <version>${project.version}</version>
+      </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
         <artifactId>camel-testng</artifactId>
diff --git 
a/platforms/spring-boot/components-starter/camel-consul-starter/pom.xml 
b/platforms/spring-boot/components-starter/camel-consul-starter/pom.xml
index f8787754752..2e34fe64095 100644
--- a/platforms/spring-boot/components-starter/camel-consul-starter/pom.xml
+++ b/platforms/spring-boot/components-starter/camel-consul-starter/pom.xml
@@ -46,11 +46,10 @@
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
-    <!-- testing (docker) -->
     <dependency>
-      <groupId>org.testcontainers</groupId>
-      <artifactId>testcontainers</artifactId>
-      <version>${testcontainers-version}</version>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-testcontainers</artifactId>
+      <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
     <!--START OF GENERATED CODE-->
diff --git 
a/platforms/spring-boot/components-starter/camel-consul-starter/src/test/java/org/apache/camel/component/consul/springboot/cloud/ConsulServiceRegistryIT.java
 
b/platforms/spring-boot/components-starter/camel-consul-starter/src/test/java/org/apache/camel/component/consul/springboot/cloud/ConsulServiceRegistryIT.java
index b3045d92144..3b8892b0ecd 100644
--- 
a/platforms/spring-boot/components-starter/camel-consul-starter/src/test/java/org/apache/camel/component/consul/springboot/cloud/ConsulServiceRegistryIT.java
+++ 
b/platforms/spring-boot/components-starter/camel-consul-starter/src/test/java/org/apache/camel/component/consul/springboot/cloud/ConsulServiceRegistryIT.java
@@ -23,29 +23,48 @@
 import com.orbitz.consul.model.catalog.CatalogService;
 import org.apache.camel.CamelContext;
 import org.apache.camel.cloud.ServiceRegistry;
-import 
org.apache.camel.component.consul.springboot.cloud.support.ConsulContainerSupport;
 import org.apache.camel.impl.cloud.DefaultServiceDefinition;
+import org.apache.camel.test.testcontainers.Wait;
 import org.junit.Rule;
 import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.test.context.runner.ApplicationContextRunner;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.util.SocketUtils;
 import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
 public class ConsulServiceRegistryIT {
-    protected static final String SERVICE_ID = UUID.randomUUID().toString();
-    protected static final String SERVICE_NAME = "my-service";
-    protected static final String SERVICE_HOST = "localhost";
-    protected static final int SERVICE_PORT = 
SocketUtils.findAvailableTcpPort();
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(ConsulServiceRegistryIT.class);
+    private static final String SERVICE_ID = UUID.randomUUID().toString();
+    private static final String SERVICE_NAME = "my-service";
+    private static final String SERVICE_HOST = "localhost";
+    private static final int SERVICE_PORT = SocketUtils.findAvailableTcpPort();
 
     @Rule
-    public GenericContainer container = 
ConsulContainerSupport.consulContainer();
+    public GenericContainer container = new GenericContainer("consul:1.0.0")
+        .withExposedPorts(Consul.DEFAULT_HTTP_PORT)
+        .waitingFor(Wait.forLogMessageContaining("Synced node info", 1))
+        .withLogConsumer(new Slf4jLogConsumer(LOGGER).withPrefix("consul"))
+        .withCommand(
+            "agent",
+            "-dev",
+            "-server",
+            "-bootstrap",
+            "-client",
+            "0.0.0.0",
+            "-log-level",
+            "trace"
+        );
 
     @Test
     public void testServiceRegistry() {
+        final String consulUrl = String.format("http://%s:%d";, 
container.getContainerIpAddress(), 
container.getMappedPort(Consul.DEFAULT_HTTP_PORT));
+
         new ApplicationContextRunner()
             .withUserConfiguration(TestConfiguration.class)
             .withPropertyValues(
@@ -53,7 +72,7 @@ public void testServiceRegistry() {
                 "spring.main.banner-mode=OFF",
                 "spring.application.name=" + UUID.randomUUID().toString(),
                 "camel.component.consul.service-registry.enabled=true",
-                "camel.component.consul.service-registry.url=" + 
ConsulContainerSupport.consulUrl(container),
+                "camel.component.consul.service-registry.url=" + consulUrl,
                 "camel.component.consul.service-registry.id=" + 
UUID.randomUUID().toString(),
                 
"camel.component.consul.service-registry.service-host=localhost")
             .run(
@@ -75,7 +94,7 @@ public void testServiceRegistry() {
                             .build()
                     );
 
-                    final Consul client = 
Consul.builder().withUrl(ConsulContainerSupport.consulUrl(container)).build();
+                    final Consul client = 
Consul.builder().withUrl(consulUrl).build();
                     final List<CatalogService> services = 
client.catalogClient().getService(SERVICE_NAME).getResponse();
 
                     assertThat(services).hasSize(1);
diff --git 
a/platforms/spring-boot/components-starter/camel-consul-starter/src/test/java/org/apache/camel/component/consul/springboot/cloud/support/ConsulContainerLogger.java
 
b/platforms/spring-boot/components-starter/camel-consul-starter/src/test/java/org/apache/camel/component/consul/springboot/cloud/support/ConsulContainerLogger.java
deleted file mode 100644
index 533c3cf5a3a..00000000000
--- 
a/platforms/spring-boot/components-starter/camel-consul-starter/src/test/java/org/apache/camel/component/consul/springboot/cloud/support/ConsulContainerLogger.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.component.consul.springboot.cloud.support;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.testcontainers.containers.output.Slf4jLogConsumer;
-
-public final class ConsulContainerLogger extends Slf4jLogConsumer {
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(ConsulContainerLogger.class);
-
-    public ConsulContainerLogger() {
-        super(LOGGER);
-
-        withPrefix("consul");
-    }
-}
diff --git 
a/platforms/spring-boot/components-starter/camel-consul-starter/src/test/java/org/apache/camel/component/consul/springboot/cloud/support/ConsulContainerSupport.java
 
b/platforms/spring-boot/components-starter/camel-consul-starter/src/test/java/org/apache/camel/component/consul/springboot/cloud/support/ConsulContainerSupport.java
deleted file mode 100644
index 9a101de14ff..00000000000
--- 
a/platforms/spring-boot/components-starter/camel-consul-starter/src/test/java/org/apache/camel/component/consul/springboot/cloud/support/ConsulContainerSupport.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.component.consul.springboot.cloud.support;
-
-import com.orbitz.consul.Consul;
-import org.testcontainers.containers.GenericContainer;
-
-public final class ConsulContainerSupport {
-    private ConsulContainerSupport() {
-    }
-
-    public static GenericContainer consulContainer() {
-        return new GenericContainer("consul:1.0.0")
-            .withExposedPorts(Consul.DEFAULT_HTTP_PORT)
-            .waitingFor(new ConsulContainerWaitStrategy())
-            .withLogConsumer(new ConsulContainerLogger())
-            .withCommand(
-                "agent",
-                "-dev",
-                "-server",
-                "-bootstrap",
-                "-client",
-                "0.0.0.0",
-                "-log-level",
-                "trace"
-            );
-    }
-
-    public static  String consulUrl(GenericContainer container) {
-        return String.format("http://%s:%d";, 
container.getContainerIpAddress(), 
container.getMappedPort(Consul.DEFAULT_HTTP_PORT));
-    }
-}
diff --git 
a/platforms/spring-boot/components-starter/camel-consul-starter/src/test/java/org/apache/camel/component/consul/springboot/cloud/support/ConsulContainerWaitStrategy.java
 
b/platforms/spring-boot/components-starter/camel-consul-starter/src/test/java/org/apache/camel/component/consul/springboot/cloud/support/ConsulContainerWaitStrategy.java
deleted file mode 100644
index 79633b075af..00000000000
--- 
a/platforms/spring-boot/components-starter/camel-consul-starter/src/test/java/org/apache/camel/component/consul/springboot/cloud/support/ConsulContainerWaitStrategy.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.camel.component.consul.springboot.cloud.support;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import com.github.dockerjava.api.DockerClient;
-import org.testcontainers.DockerClientFactory;
-import org.testcontainers.containers.ContainerLaunchException;
-import org.testcontainers.containers.output.WaitingConsumer;
-import org.testcontainers.containers.wait.strategy.AbstractWaitStrategy;
-import org.testcontainers.utility.LogUtils;
-
-public final class ConsulContainerWaitStrategy extends AbstractWaitStrategy {
-    @Override
-    protected void waitUntilReady() {
-        final DockerClient client = DockerClientFactory.instance().client();
-        final WaitingConsumer waitingConsumer = new WaitingConsumer();
-
-        LogUtils.followOutput(client, waitStrategyTarget.getContainerId(), 
waitingConsumer);
-
-        try {
-            waitingConsumer.waitUntil(
-                f -> f.getUtf8String().contains("Synced node info"),
-                startupTimeout.getSeconds(),
-                TimeUnit.SECONDS,
-                1
-            );
-        } catch (TimeoutException e) {
-            throw new ContainerLaunchException("Timed out");
-        }
-    }
-}
\ No newline at end of file
diff --git 
a/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml 
b/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml
index 090c6ad95e7..9d51c71f68f 100644
--- 
a/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml
+++ 
b/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml
@@ -2819,6 +2819,16 @@
         <artifactId>camel-test-spring</artifactId>
         <version>${project.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-testcontainers</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-testcontainers-spring</artifactId>
+        <version>${project.version}</version>
+      </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
         <artifactId>camel-testng</artifactId>
diff --git 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootStarterMojo.java
 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootStarterMojo.java
index 4b94e2fe94f..e561e9dda66 100644
--- 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootStarterMojo.java
+++ 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/SpringBootStarterMojo.java
@@ -567,7 +567,7 @@ private boolean isStarterAllowed() {
             }
         }
 
-        if (IGNORE_TEST_MODULES && 
(project.getArtifactId().startsWith("camel-test") || 
project.getArtifactId().startsWith("camel-testng"))) {
+        if (IGNORE_TEST_MODULES && 
(project.getArtifactId().startsWith("camel-test") || 
project.getArtifactId().startsWith("camel-testng") || 
project.getArtifactId().startsWith("camel-testcontainers"))) {
             getLog().debug("Test components are ignored");
             return false;
         }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


> create camel-testcontainers
> ---------------------------
>
>                 Key: CAMEL-12534
>                 URL: https://issues.apache.org/jira/browse/CAMEL-12534
>             Project: Camel
>          Issue Type: New Feature
>            Reporter: Luca Burgazzoli
>            Assignee: Luca Burgazzoli
>            Priority: Minor
>             Fix For: 2.22.0
>
>
> I've been using testcontainers [1] for a while and I found it useful
> to test against non java services such as consul, etcd and so on so
> I'd like to create a camel-testcontainers "component" that includes
> some facilities like a dedicated test support that take care of
> starting/stopping containers.
> [1] https://www.testcontainers.org/



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)

Reply via email to