You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by al...@apache.org on 2019/08/06 14:36:55 UTC

[camel] branch CAMEL-13826 created (now 8e54095)

This is an automated email from the ASF dual-hosted git repository.

aldettinger pushed a change to branch CAMEL-13826
in repository https://gitbox.apache.org/repos/asf/camel.git.


      at 8e54095  CAMEL-13826: Initial version of a camel-test module with JUnit 5

This branch includes the following new commits:

     new 8e54095  CAMEL-13826: Initial version of a camel-test module with JUnit 5

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[camel] 01/01: CAMEL-13826: Initial version of a camel-test module with JUnit 5

Posted by al...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

aldettinger pushed a commit to branch CAMEL-13826
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 8e54095dca16ef97e34236966faa54b232ba5601
Author: aldettinger <al...@gmail.com>
AuthorDate: Tue Aug 6 16:32:34 2019 +0200

    CAMEL-13826: Initial version of a camel-test module with JUnit 5
---
 components/camel-test-junit5/pom.xml               |   73 ++
 .../src/main/docs/test-junit5.adoc                 |   46 +
 .../org/apache/camel/test/AvailablePortFinder.java |  170 +++
 .../test/ExcludingPackageScanClassResolver.java    |   33 +
 .../camel/test/TestSupportNodeIdFactory.java       |   30 +
 .../apache/camel/test/junit5/CamelTestSupport.java | 1268 ++++++++++++++++++++
 .../org/apache/camel/test/junit5/TestSupport.java  |  543 +++++++++
 .../apache/camel/test/AvailablePortFinderTest.java |   68 ++
 .../camel/test/junit5/CamelTestSupportTest.java    |   76 ++
 .../test/junit5/RouteFilterPatternExcludeTest.java |   57 +
 .../RouteFilterPatternIncludeExcludeTest.java      |   64 +
 .../test/junit5/RouteFilterPatternIncludeTest.java |   57 +
 .../test/junit5/patterns/AdviceWithLambdaTest.java |   56 +
 .../junit5/patterns/AdviceWithNotStartedTest.java  |   73 ++
 .../test/junit5/patterns/AsyncSendMockTest.java    |   47 +
 .../CreateCamelContextPerTestFalseTest.java        |  119 ++
 .../CreateCamelContextPerTestTrueTest.java         |  153 +++
 .../test/junit5/patterns/DebugJUnit5Test.java      |   84 ++
 .../patterns/DebugNoLazyTypeConverterTest.java     |   89 ++
 .../camel/test/junit5/patterns/DebugTest.java      |   84 ++
 .../FilterCreateCamelContextPerClassTest.java      |   65 +
 .../junit5/patterns/FilterFluentTemplateTest.java  |   75 ++
 .../test/junit5/patterns/FilterJUnit5Test.java     |   69 ++
 .../camel/test/junit5/patterns/FilterTest.java     |   75 ++
 .../test/junit5/patterns/GetMockEndpointTest.java  |   45 +
 .../patterns/IsMockEndpointsAndSkipJUnit5Test.java |   68 ++
 .../junit5/patterns/IsMockEndpointsFileTest.java   |   68 ++
 .../junit5/patterns/IsMockEndpointsJUnit5Test.java |   74 ++
 .../test/junit5/patterns/IsMockEndpointsTest.java  |   65 +
 .../patterns/MockEndpointFailNoHeaderTest.java     |   63 +
 .../camel/test/junit5/patterns/MyProduceBean.java  |   32 +
 .../camel/test/junit5/patterns/MySender.java       |   25 +
 .../test/junit5/patterns/ProduceBeanTest.java      |   46 +
 .../RouteBuilderConfigureExceptionTest.java        |   54 +
 .../RouteProcessorDumpRouteCoverageTest.java       |   58 +
 .../junit5/patterns/SimpleMockEndpointsTest.java   |   50 +
 .../camel/test/junit5/patterns/SimpleMockTest.java |   45 +
 .../junit5/patterns/SimpleNotifyBuilderTest.java   |   53 +
 .../patterns/SimpleWeaveAddMockLastTest.java       |   59 +
 ...rridePropertiesWithPropertiesComponentTest.java |   75 ++
 .../src/test/resources/jndi.properties             |   22 +
 .../src/test/resources/log4j2.properties           |   30 +
 components/pom.xml                                 |    1 +
 43 files changed, 4407 insertions(+)

diff --git a/components/camel-test-junit5/pom.xml b/components/camel-test-junit5/pom.xml
new file mode 100644
index 0000000..1300568
--- /dev/null
+++ b/components/camel-test-junit5/pom.xml
@@ -0,0 +1,73 @@
+<?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:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns="http://maven.apache.org/POM/4.0.0"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.camel</groupId>
+		<artifactId>components</artifactId>
+		<version>3.0.0-SNAPSHOT</version>
+	</parent>
+
+	<artifactId>camel-test-junit5</artifactId>
+	<packaging>jar</packaging>
+
+	<name>Camel :: Test :: JUnit5</name>
+	<description>Camel unit testing with JUnit 5</description>
+
+	<properties>
+		<firstVersion>3.0.0</firstVersion>
+		<label>testing,java</label>
+
+	</properties>
+
+	<dependencies>
+
+		<dependency>
+			<groupId>org.apache.camel</groupId>
+			<artifactId>camel-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.camel</groupId>
+			<artifactId>camel-management-impl</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.junit.jupiter</groupId>
+			<artifactId>junit-jupiter-api</artifactId>
+			<version>5.4.2</version>
+		</dependency>
+		<dependency>
+			<groupId>org.junit.jupiter</groupId>
+			<artifactId>junit-jupiter-engine</artifactId>
+			<version>5.4.2</version>
+		</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>
+</project>
diff --git a/components/camel-test-junit5/src/main/docs/test-junit5.adoc b/components/camel-test-junit5/src/main/docs/test-junit5.adoc
new file mode 100644
index 0000000..6624d90
--- /dev/null
+++ b/components/camel-test-junit5/src/main/docs/test-junit5.adoc
@@ -0,0 +1,46 @@
+[[Test]]
+= Test Module
+
+The `camel-test-junit5` module is used for unit testing Camel.
+
+The class `org.apache.camel.test.junit5.CamelTestSupport` provides a base JUnit class which you would extend
+and implement your Camel unit test.
+
+== Simple unit test example
+
+As shown below is a basic junit test which uses `camel-test-junit5`. The `createRouteBuilder` method is used
+for build the routes to be tested. Then the methods with `@Test` annotations are JUnit test methods which
+will be executed. The base class `CamelTestSupport` has a number of helper methods to configure testing,
+see more at the javadoc of this class.
+
+[source,java]
+----
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class SimpleMockTest extends CamelTestSupport {
+
+    @Test
+    public void testMock() throws Exception {
+        getMockEndpoint("mock:result").expectedBodiesReceived("Hello World");
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start").to("mock:result");
+            }
+        };
+    }
+
+}
+----
+
diff --git a/components/camel-test-junit5/src/main/java/org/apache/camel/test/AvailablePortFinder.java b/components/camel-test-junit5/src/main/java/org/apache/camel/test/AvailablePortFinder.java
new file mode 100644
index 0000000..602bea6
--- /dev/null
+++ b/components/camel-test-junit5/src/main/java/org/apache/camel/test/AvailablePortFinder.java
@@ -0,0 +1,170 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.ServerSocket;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Finds currently available server ports.
+ */
+public final class AvailablePortFinder {
+
+    /**
+     * The minimum server currentMinPort number for IPv4. Set at 1100 to avoid
+     * returning privileged currentMinPort numbers.
+     */
+    public static final int MIN_PORT_NUMBER = 1100;
+
+    /**
+     * The maximum server currentMinPort number for IPv4.
+     */
+    public static final int MAX_PORT_NUMBER = 65535;
+
+    private static final Logger LOG = LoggerFactory.getLogger(AvailablePortFinder.class);
+
+    /**
+     * We'll hold open the lowest port in this process so parallel processes
+     * won't use the same block of ports. They'll go up to the next block.
+     */
+    private static final ServerSocket LOCK;
+
+    /**
+     * Incremented to the next lowest available port when getNextAvailable() is
+     * called.
+     */
+    private static AtomicInteger currentMinPort = new AtomicInteger(MIN_PORT_NUMBER);
+
+    /**
+     * Creates a new instance.
+     */
+    private AvailablePortFinder() {
+        // Do nothing
+    }
+
+    static {
+        int port = MIN_PORT_NUMBER;
+        ServerSocket serverSocker = null;
+
+        while (serverSocker == null) {
+            try {
+                serverSocker = new ServerSocket(port);
+            } catch (Exception e) {
+                serverSocker = null;
+                port += 200;
+            }
+        }
+        LOCK = serverSocker;
+        Runtime.getRuntime().addShutdownHook(new Thread() {
+            public void run() {
+                try {
+                    LOCK.close();
+                } catch (Exception ex) {
+                    // ignore
+                }
+            }
+        });
+        currentMinPort.set(port + 1);
+    }
+
+    /**
+     * Gets the next available port starting at the lowest number. This is the
+     * preferred method to use. The port return is immediately marked in use and
+     * doesn't rely on the caller actually opening the port.
+     *
+     * @throws IllegalArgumentException is thrown if the port number is out of
+     *             range
+     * @throws NoSuchElementException if there are no ports available
+     * @return the available port
+     */
+    public static synchronized int getNextAvailable() {
+        int next = getNextAvailable(currentMinPort.get());
+        currentMinPort.set(next + 1);
+        return next;
+    }
+
+    /**
+     * Gets the next available port starting at a given from port.
+     *
+     * @param fromPort the from port to scan for availability
+     * @throws IllegalArgumentException is thrown if the port number is out of
+     *             range
+     * @throws NoSuchElementException if there are no ports available
+     * @return the available port
+     */
+    public static synchronized int getNextAvailable(int fromPort) {
+        if (fromPort < currentMinPort.get() || fromPort > MAX_PORT_NUMBER) {
+            throw new IllegalArgumentException("From port number not in valid range: " + fromPort);
+        }
+
+        for (int currentPort = fromPort; currentPort <= MAX_PORT_NUMBER; currentPort++) {
+            if (available(currentPort)) {
+                LOG.info("getNextAvailable({}) -> {}", fromPort, currentPort);
+                return currentPort;
+            }
+        }
+
+        throw new NoSuchElementException("Could not find an available port above " + fromPort);
+    }
+
+    /**
+     * Checks to see if a specific port is available.
+     *
+     * @param port the port number to check for availability
+     * @return <tt>true</tt> if the port is available, or <tt>false</tt> if not
+     * @throws IllegalArgumentException is thrown if the port number is out of
+     *             range
+     */
+    public static boolean available(int port) throws IllegalArgumentException {
+        if (port < currentMinPort.get() || port > MAX_PORT_NUMBER) {
+            throw new IllegalArgumentException("Invalid start currentMinPort: " + port);
+        }
+
+        ServerSocket serverSocket = null;
+        DatagramSocket datagramSocket = null;
+        try {
+            serverSocket = new ServerSocket(port);
+            serverSocket.setReuseAddress(true);
+            datagramSocket = new DatagramSocket(port);
+            datagramSocket.setReuseAddress(true);
+            return true;
+        } catch (IOException e) {
+            // Do nothing
+        } finally {
+            if (datagramSocket != null) {
+                datagramSocket.close();
+            }
+
+            if (serverSocket != null) {
+                try {
+                    serverSocket.close();
+                } catch (IOException e) {
+                    /* should not be thrown */
+                }
+            }
+        }
+
+        return false;
+    }
+
+}
diff --git a/components/camel-test-junit5/src/main/java/org/apache/camel/test/ExcludingPackageScanClassResolver.java b/components/camel-test-junit5/src/main/java/org/apache/camel/test/ExcludingPackageScanClassResolver.java
new file mode 100644
index 0000000..ce04f9e
--- /dev/null
+++ b/components/camel-test-junit5/src/main/java/org/apache/camel/test/ExcludingPackageScanClassResolver.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.camel.impl.engine.DefaultPackageScanClassResolver;
+import org.apache.camel.impl.scan.AssignableToPackageScanFilter;
+import org.apache.camel.impl.scan.InvertingPackageScanFilter;
+
+public class ExcludingPackageScanClassResolver extends DefaultPackageScanClassResolver {
+
+    public void setExcludedClasses(Set<Class<?>> excludedClasses) {
+        Set<Class<?>> parents = excludedClasses == null ? Collections.<Class<?>> emptySet() : excludedClasses;
+        addFilter(new InvertingPackageScanFilter(new AssignableToPackageScanFilter(parents)));
+    }
+
+}
diff --git a/components/camel-test-junit5/src/main/java/org/apache/camel/test/TestSupportNodeIdFactory.java b/components/camel-test-junit5/src/main/java/org/apache/camel/test/TestSupportNodeIdFactory.java
new file mode 100644
index 0000000..e66d42a
--- /dev/null
+++ b/components/camel-test-junit5/src/main/java/org/apache/camel/test/TestSupportNodeIdFactory.java
@@ -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.
+ */
+package org.apache.camel.test;
+
+import org.apache.camel.impl.engine.DefaultNodeIdFactory;
+
+public class TestSupportNodeIdFactory extends DefaultNodeIdFactory {
+
+    /**
+     * Start tests with fresh counters
+     */
+    public static void resetCounters() {
+        resetAllCounters();
+    }
+
+}
diff --git a/components/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/CamelTestSupport.java b/components/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/CamelTestSupport.java
new file mode 100644
index 0000000..ff5c85b
--- /dev/null
+++ b/components/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/CamelTestSupport.java
@@ -0,0 +1,1268 @@
+/*
+ * 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.junit5;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+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;
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ConsumerTemplate;
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.FluentProducerTemplate;
+import org.apache.camel.Message;
+import org.apache.camel.NamedNode;
+import org.apache.camel.NoSuchEndpointException;
+import org.apache.camel.Predicate;
+import org.apache.camel.Processor;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.Route;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.Service;
+import org.apache.camel.ServiceStatus;
+import org.apache.camel.api.management.JmxSystemPropertyKeys;
+import org.apache.camel.api.management.ManagedCamelContext;
+import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
+import org.apache.camel.api.management.mbean.ManagedProcessorMBean;
+import org.apache.camel.api.management.mbean.ManagedRouteMBean;
+import org.apache.camel.builder.AdviceWithRouteBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.component.properties.PropertiesComponent;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.impl.JndiRegistry;
+import org.apache.camel.impl.engine.InterceptSendToMockEndpointStrategy;
+import org.apache.camel.model.Model;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.processor.interceptor.BreakpointSupport;
+import org.apache.camel.processor.interceptor.DefaultDebugger;
+import org.apache.camel.reifier.RouteReifier;
+import org.apache.camel.spi.CamelBeanPostProcessor;
+import org.apache.camel.spi.Language;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.support.EndpointHelper;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.TimeUtils;
+import org.apache.camel.util.URISupport;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * A useful base class which creates a {@link org.apache.camel.CamelContext}
+ * with some routes along with a {@link org.apache.camel.ProducerTemplate} for
+ * use in the test case Do <tt>not</tt> use this class for Spring Boot testing.
+ */
+public abstract class CamelTestSupport extends TestSupport implements BeforeEachCallback, AfterAllCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback {
+
+    /**
+     * JVM system property which can be set to true to turn on dumping route
+     * coverage statistics.
+     */
+    public static final String ROUTE_COVERAGE_ENABLED = "CamelTestRouteCoverage";
+
+    // CHECKSTYLE:OFF
+    private static final Logger LOG = LoggerFactory.getLogger(CamelTestSupport.class);
+    private static ThreadLocal<ModelCamelContext> threadCamelContext = new ThreadLocal<>();
+    private static ThreadLocal<ProducerTemplate> threadTemplate = new ThreadLocal<>();
+    private static ThreadLocal<FluentProducerTemplate> threadFluentTemplate = new ThreadLocal<>();
+    private static ThreadLocal<ConsumerTemplate> threadConsumer = new ThreadLocal<>();
+    private static ThreadLocal<Service> threadService = new ThreadLocal<>();
+    protected Properties extra;
+    protected volatile ModelCamelContext context;
+    protected volatile ProducerTemplate template;
+    protected volatile FluentProducerTemplate fluentTemplate;
+    protected volatile ConsumerTemplate consumer;
+    protected volatile Service camelContextService;
+    @RegisterExtension
+    protected CamelTestSupport camelTestSupportExtension = this;
+    private boolean useRouteBuilder = true;
+    private final DebugBreakpoint breakpoint = new DebugBreakpoint();
+    private final StopWatch watch = new StopWatch();
+    private final Map<String, String> fromEndpoints = new HashMap<>();
+    private static final ThreadLocal<AtomicInteger> TESTS = new ThreadLocal<>();
+    private static final ThreadLocal<CamelTestSupport> INSTANCE = new ThreadLocal<>();
+    private String currentTestName;
+    // CHECKSTYLE:ON
+
+    @Override
+    public void afterTestExecution(ExtensionContext context) throws Exception {
+        watch.taken();
+    }
+
+    @Override
+    public void beforeTestExecution(ExtensionContext context) throws Exception {
+        watch.restart();
+    }
+
+    public long timeTaken() {
+        return watch.taken();
+    }
+
+    @Override
+    public void beforeEach(ExtensionContext context) throws Exception {
+        currentTestName = context.getDisplayName();
+    }
+
+    @Override
+    public void afterAll(ExtensionContext context) {
+        CamelTestSupport support = INSTANCE.get();
+        if (support != null && support.isCreateCamelContextPerClass()) {
+            try {
+                support.tearDownCreateCamelContextPerClass();
+            } catch (Throwable e) {
+                // ignore
+            }
+        }
+    }
+
+    /**
+     * Use the RouteBuilder or not
+     *
+     * @return <tt>true</tt> then {@link CamelContext} will be auto started,
+     *         <tt>false</tt> then {@link CamelContext} will <b>not</b> be auto
+     *         started (you will have to start it manually)
+     */
+    public boolean isUseRouteBuilder() {
+        return useRouteBuilder;
+    }
+
+    public void setUseRouteBuilder(boolean useRouteBuilder) {
+        this.useRouteBuilder = useRouteBuilder;
+    }
+
+    /**
+     * Whether to dump route coverage stats at the end of the test.
+     * <p/>
+     * This allows tooling or manual inspection of the stats, so you can
+     * generate a route trace diagram of which EIPs have been in use and which
+     * have not. Similar concepts as a code coverage report.
+     * <p/>
+     * You can also turn on route coverage globally via setting JVM system
+     * property <tt>CamelTestRouteCoverage=true</tt>.
+     *
+     * @return <tt>true</tt> to write route coverage status in an xml file in
+     *         the <tt>target/camel-route-coverage</tt> directory after the test
+     *         has finished.
+     */
+    public boolean isDumpRouteCoverage() {
+        return false;
+    }
+
+    /**
+     * Override when using
+     * <a href="http://camel.apache.org/advicewith.html">advice with</a> and
+     * return <tt>true</tt>. This helps knowing advice with is to be used, and
+     * {@link CamelContext} will not be started before the advice with takes
+     * place. This helps by ensuring the advice with has been property setup
+     * before the {@link CamelContext} is started
+     * <p/>
+     * <b>Important:</b> Its important to start {@link CamelContext} manually
+     * from the unit test after you are done doing all the advice with.
+     *
+     * @return <tt>true</tt> if you use advice with in your unit tests.
+     */
+    public boolean isUseAdviceWith() {
+        return false;
+    }
+
+    /**
+     * Override to control whether {@link CamelContext} should be setup per test
+     * or per class.
+     * <p/>
+     * By default it will be setup/teardown per test (per test method). If you
+     * want to re-use {@link CamelContext} between test methods you can override
+     * this method and return <tt>true</tt>
+     * <p/>
+     * <b>Important:</b> Use this with care as the {@link CamelContext} will
+     * carry over state from previous tests, such as endpoints, components etc.
+     * So you cannot use this in all your tests.
+     * <p/>
+     * Setting up {@link CamelContext} uses the {@link #doPreSetup()},
+     * {@link #doSetUp()}, and {@link #doPostSetup()} methods in that given
+     * order.
+     *
+     * @return <tt>true</tt> per class, <tt>false</tt> per test.
+     */
+    public boolean isCreateCamelContextPerClass() {
+        return false;
+    }
+
+    /**
+     * Override to enable auto mocking endpoints based on the pattern.
+     * <p/>
+     * Return <tt>*</tt> to mock all endpoints.
+     *
+     * @see EndpointHelper#matchEndpoint(CamelContext, String, String)
+     */
+    public String isMockEndpoints() {
+        return null;
+    }
+
+    /**
+     * Override to enable auto mocking endpoints based on the pattern, and
+     * <b>skip</b> sending to original endpoint.
+     * <p/>
+     * Return <tt>*</tt> to mock all endpoints.
+     *
+     * @see EndpointHelper#matchEndpoint(CamelContext, String, String)
+     */
+    public String isMockEndpointsAndSkip() {
+        return null;
+    }
+
+    public void replaceRouteFromWith(String routeId, String fromEndpoint) {
+        fromEndpoints.put(routeId, fromEndpoint);
+    }
+
+    /**
+     * Used for filtering routes matching the given pattern, which follows the
+     * following rules:
+     * <p>
+     * - Match by route id - Match by route input endpoint uri
+     * <p>
+     * The matching is using exact match, by wildcard and regular expression.
+     * <p>
+     * For example to only include routes which starts with foo in their route
+     * id's, use: include=foo&#42; And to exclude routes which starts from JMS
+     * endpoints, use: exclude=jms:&#42;
+     * <p>
+     * Multiple patterns can be separated by comma, for example to exclude both
+     * foo and bar routes, use: exclude=foo&#42;,bar&#42;
+     * <p>
+     * Exclude takes precedence over include.
+     */
+    public String getRouteFilterIncludePattern() {
+        return null;
+    }
+
+    /**
+     * Used for filtering routes matching the given pattern, which follows the
+     * following rules:
+     * <p>
+     * - Match by route id - Match by route input endpoint uri
+     * <p>
+     * The matching is using exact match, by wildcard and regular expression.
+     * <p>
+     * For example to only include routes which starts with foo in their route
+     * id's, use: include=foo&#42; And to exclude routes which starts from JMS
+     * endpoints, use: exclude=jms:&#42;
+     * <p>
+     * Multiple patterns can be separated by comma, for example to exclude both
+     * foo and bar routes, use: exclude=foo&#42;,bar&#42;
+     * <p>
+     * Exclude takes precedence over include.
+     */
+    public String getRouteFilterExcludePattern() {
+        return null;
+    }
+
+    /**
+     * Override to enable debugger
+     * <p/>
+     * Is default <tt>false</tt>
+     */
+    public boolean isUseDebugger() {
+        return false;
+    }
+
+    public Service getCamelContextService() {
+        return camelContextService;
+    }
+
+    public Service camelContextService() {
+        return camelContextService;
+    }
+
+    public CamelContext context() {
+        return context;
+    }
+
+    public ProducerTemplate template() {
+        return template;
+    }
+
+    public FluentProducerTemplate fluentTemplate() {
+        return fluentTemplate;
+    }
+
+    public ConsumerTemplate consumer() {
+        return consumer;
+    }
+
+    /**
+     * Allows a service to be registered a separate lifecycle service to start
+     * and stop the context; such as for Spring when the ApplicationContext is
+     * started and stopped, rather than directly stopping the CamelContext
+     */
+    public void setCamelContextService(Service service) {
+        camelContextService = service;
+        threadService.set(camelContextService);
+    }
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        log.info("********************************************************************************");
+        log.info("Testing: " + currentTestName + "(" + getClass().getName() + ")");
+        log.info("********************************************************************************");
+
+        if (isCreateCamelContextPerClass()) {
+            INSTANCE.set(this);
+            AtomicInteger v = TESTS.get();
+            if (v == null) {
+                v = new AtomicInteger();
+                TESTS.set(v);
+            }
+            if (v.getAndIncrement() == 0) {
+                LOG.debug("Setup CamelContext before running first test");
+                // test is per class, so only setup once (the first time)
+                doSpringBootCheck();
+                setupResources();
+                doPreSetup();
+                doSetUp();
+                doPostSetup();
+            } else {
+                LOG.debug("Reset between test methods");
+                // and in between tests we must do IoC and reset mocks
+                postProcessTest();
+                resetMocks();
+            }
+        } else {
+            // test is per test so always setup
+            doSpringBootCheck();
+            setupResources();
+            doPreSetup();
+            doSetUp();
+            doPostSetup();
+        }
+
+        // only start timing after all the setup
+        watch.restart();
+    }
+
+    /**
+     * Strategy to perform any pre setup, before {@link CamelContext} is created
+     */
+    protected void doPreSetup() throws Exception {
+        // noop
+    }
+
+    /**
+     * Strategy to perform any post setup after {@link CamelContext} is created
+     */
+    protected void doPostSetup() throws Exception {
+        // noop
+    }
+
+    /**
+     * Detects if this is a Spring-Boot test and throws an exception, as these
+     * base classes is not intended for testing Camel on Spring Boot.
+     */
+    protected void doSpringBootCheck() {
+        boolean springBoot = hasClassAnnotation("org.springframework.boot.test.context.SpringBootTest");
+        if (springBoot) {
+            throw new RuntimeException("Spring Boot detected: The CamelTestSupport/CamelSpringTestSupport class is not intended for Camel testing with Spring Boot.");
+        }
+    }
+
+    private void doSetUp() throws Exception {
+        log.debug("setUp test");
+        // jmx is enabled if we have configured to use it, or if dump route
+        // coverage is enabled (it requires JMX)
+        boolean jmx = useJmx() || isRouteCoverageEnabled();
+        if (jmx) {
+            enableJMX();
+        } else {
+            disableJMX();
+        }
+
+        context = (ModelCamelContext)createCamelContext();
+        threadCamelContext.set(context);
+
+        assertNotNull(context, "No context found!");
+
+        // add custom beans
+        bindToRegistry(context.getRegistry());
+
+        // reduce default shutdown timeout to avoid waiting for 300 seconds
+        context.getShutdownStrategy().setTimeout(getShutdownTimeout());
+
+        // set debugger if enabled
+        if (isUseDebugger()) {
+            if (context.getStatus().equals(ServiceStatus.Started)) {
+                log.info("Cannot setting the Debugger to the starting CamelContext, stop the CamelContext now.");
+                // we need to stop the context first to setup the debugger
+                context.stop();
+            }
+            context.setDebugger(new DefaultDebugger());
+            context.getDebugger().addBreakpoint(breakpoint);
+            // when stopping CamelContext it will automatic remove the
+            // breakpoint
+        }
+
+        template = context.createProducerTemplate();
+        template.start();
+        fluentTemplate = context.createFluentProducerTemplate();
+        fluentTemplate.start();
+        consumer = context.createConsumerTemplate();
+        consumer.start();
+
+        threadTemplate.set(template);
+        threadFluentTemplate.set(fluentTemplate);
+        threadConsumer.set(consumer);
+
+        // enable auto mocking if enabled
+        String pattern = isMockEndpoints();
+        if (pattern != null) {
+            context.adapt(ExtendedCamelContext.class).registerEndpointCallback(new InterceptSendToMockEndpointStrategy(pattern));
+        }
+        pattern = isMockEndpointsAndSkip();
+        if (pattern != null) {
+            context.adapt(ExtendedCamelContext.class).registerEndpointCallback(new InterceptSendToMockEndpointStrategy(pattern, true));
+        }
+
+        // configure properties component (mandatory for testing)
+        PropertiesComponent pc = context.getComponent("properties", PropertiesComponent.class);
+        if (extra == null) {
+            extra = useOverridePropertiesWithPropertiesComponent();
+        }
+        if (extra != null && !extra.isEmpty()) {
+            pc.setOverrideProperties(extra);
+        }
+        Boolean ignore = ignoreMissingLocationWithPropertiesComponent();
+        if (ignore != null) {
+            pc.setIgnoreMissingLocation(ignore);
+        }
+
+        String include = getRouteFilterIncludePattern();
+        String exclude = getRouteFilterExcludePattern();
+        if (include != null || exclude != null) {
+            log.info("Route filtering pattern: include={}, exclude={}", include, exclude);
+            context.getExtension(Model.class).setRouteFilterPattern(include, exclude);
+        }
+
+        // prepare for in-between tests
+        postProcessTest();
+
+        if (isUseRouteBuilder()) {
+            RoutesBuilder[] builders = createRouteBuilders();
+            for (RoutesBuilder builder : builders) {
+                log.debug("Using created route builder: " + builder);
+                context.addRoutes(builder);
+            }
+            replaceFromEndpoints();
+            boolean skip = "true".equalsIgnoreCase(System.getProperty("skipStartingCamelContext"));
+            if (skip) {
+                log.info("Skipping starting CamelContext as system property skipStartingCamelContext is set to be true.");
+            } else if (isUseAdviceWith()) {
+                log.info("Skipping starting CamelContext as isUseAdviceWith is set to true.");
+            } else {
+                startCamelContext();
+            }
+        } else {
+            replaceFromEndpoints();
+            log.debug("Using route builder from the created context: " + context);
+        }
+        log.debug("Routing Rules are: " + context.getRoutes());
+
+        assertValidContext(context);
+    }
+
+    private void replaceFromEndpoints() throws Exception {
+        for (final Map.Entry<String, String> entry : fromEndpoints.entrySet()) {
+            RouteReifier.adviceWith(context.getRouteDefinition(entry.getKey()), context, new AdviceWithRouteBuilder() {
+                @Override
+                public void configure() throws Exception {
+                    replaceFromWith(entry.getValue());
+                }
+            });
+        }
+    }
+
+    private boolean isRouteCoverageEnabled() {
+        return System.getProperty(ROUTE_COVERAGE_ENABLED, "false").equalsIgnoreCase("true") || isDumpRouteCoverage();
+    }
+
+    @AfterEach
+    public void tearDown() throws Exception {
+        long time = watch.taken();
+
+        log.info("********************************************************************************");
+        log.info("Testing done: " + currentTestName + "(" + getClass().getName() + ")");
+        log.info("Took: " + TimeUtils.printDuration(time) + " (" + time + " millis)");
+
+        // if we should dump route stats, then write that to a file
+        if (isRouteCoverageEnabled()) {
+            String className = this.getClass().getSimpleName();
+            String dir = "target/camel-route-coverage";
+            String name = className + "-" + StringHelper.before(currentTestName, "(") + ".xml";
+
+            ManagedCamelContext mc = context != null ? context.getExtension(ManagedCamelContext.class) : null;
+            ManagedCamelContextMBean managedCamelContext = mc != null ? mc.getManagedCamelContext() : null;
+            if (managedCamelContext == null) {
+                log.warn("Cannot dump route coverage to file as JMX is not enabled. "
+                         + "Add camel-management-impl JAR as dependency and/or override useJmx() method to enable JMX in the unit test classes.");
+            } else {
+                logCoverageSummary(managedCamelContext);
+
+                String xml = managedCamelContext.dumpRoutesCoverageAsXml();
+                String combined = "<camelRouteCoverage>\n" + gatherTestDetailsAsXml() + xml + "\n</camelRouteCoverage>";
+
+                File file = new File(dir);
+                // ensure dir exists
+                file.mkdirs();
+                file = new File(dir, name);
+
+                log.info("Dumping route coverage to file: {}", file);
+                InputStream is = new ByteArrayInputStream(combined.getBytes());
+                OutputStream os = new FileOutputStream(file, false);
+                IOHelper.copyAndCloseInput(is, os);
+                IOHelper.close(os);
+            }
+        }
+        log.info("********************************************************************************");
+
+        if (isCreateCamelContextPerClass()) {
+            // will tear down test specially in CamelTearDownRule
+        } else {
+            LOG.debug("tearDown()");
+            doStopTemplates(consumer, template, fluentTemplate);
+            doStopCamelContext(context, camelContextService);
+            doPostTearDown();
+            cleanupResources();
+        }
+    }
+
+    void tearDownCreateCamelContextPerClass() throws Exception {
+        LOG.debug("tearDownCreateCamelContextPerClass()");
+        TESTS.remove();
+        doStopTemplates(threadConsumer.get(), threadTemplate.get(), threadFluentTemplate.get());
+        doStopCamelContext(threadCamelContext.get(), threadService.get());
+        doPostTearDown();
+        cleanupResources();
+    }
+
+    /**
+     * 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
+    }
+
+    /**
+     * Logs route coverage summary: - which routes are uncovered - what is the
+     * coverage of each processor in each route
+     */
+    private void logCoverageSummary(ManagedCamelContextMBean managedCamelContext) throws Exception {
+        StringBuilder builder = new StringBuilder("\nCoverage summary\n");
+
+        int routes = managedCamelContext.getTotalRoutes();
+
+        long contextExchangesTotal = managedCamelContext.getExchangesTotal();
+
+        List<String> uncoveredRoutes = new ArrayList<>();
+
+        StringBuilder routesSummary = new StringBuilder();
+        routesSummary.append("\tProcessor coverage\n");
+
+        MBeanServer server = context.getManagementStrategy().getManagementAgent().getMBeanServer();
+
+        Map<String, List<ManagedProcessorMBean>> processorsForRoute = findProcessorsForEachRoute(server);
+
+        // log processor coverage for each route
+        for (Route route : context.getRoutes()) {
+            ManagedRouteMBean managedRoute = context.getExtension(ManagedCamelContext.class).getManagedRoute(route.getId());
+            if (managedRoute.getExchangesTotal() == 0) {
+                uncoveredRoutes.add(route.getId());
+            }
+
+            long routeCoveragePercentage = Math.round((double)managedRoute.getExchangesTotal() / contextExchangesTotal * 100);
+            routesSummary.append("\t\tRoute ").append(route.getId()).append(" total: ").append(managedRoute.getExchangesTotal()).append(" (").append(routeCoveragePercentage)
+                .append("%)\n");
+
+            if (server != null) {
+                List<ManagedProcessorMBean> processors = processorsForRoute.get(route.getId());
+                if (processors != null) {
+                    for (ManagedProcessorMBean managedProcessor : processors) {
+                        String processorId = managedProcessor.getProcessorId();
+                        long processorExchangesTotal = managedProcessor.getExchangesTotal();
+                        long processorCoveragePercentage = Math.round((double)processorExchangesTotal / contextExchangesTotal * 100);
+                        routesSummary.append("\t\t\tProcessor ").append(processorId).append(" total: ").append(processorExchangesTotal).append(" (")
+                            .append(processorCoveragePercentage).append("%)\n");
+                    }
+                }
+            }
+        }
+
+        int used = routes - uncoveredRoutes.size();
+
+        long contextPercentage = Math.round((double)used / routes * 100);
+        builder.append("\tRoute coverage: ").append(used).append(" out of ").append(routes).append(" routes used (").append(contextPercentage).append("%)\n");
+        builder.append("\t\tCamelContext (").append(managedCamelContext.getCamelId()).append(") total: ").append(contextExchangesTotal).append("\n");
+
+        if (uncoveredRoutes.size() > 0) {
+            builder.append("\t\tUncovered routes: ").append(uncoveredRoutes.stream().collect(Collectors.joining(", "))).append("\n");
+        }
+
+        builder.append(routesSummary);
+        log.info(builder.toString());
+    }
+
+    /**
+     * Groups all processors from Camel context by route id
+     */
+    private Map<String, List<ManagedProcessorMBean>> findProcessorsForEachRoute(MBeanServer server)
+        throws MalformedObjectNameException, MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException {
+        String domain = context.getManagementStrategy().getManagementAgent().getMBeanServerDefaultDomain();
+
+        Map<String, List<ManagedProcessorMBean>> processorsForRoute = new HashMap<>();
+
+        ObjectName processorsObjectName = new ObjectName(domain + ":context=" + context.getManagementName() + ",type=processors,name=*");
+        Set<ObjectName> objectNames = server.queryNames(processorsObjectName, null);
+
+        for (ObjectName objectName : objectNames) {
+            String routeId = server.getAttribute(objectName, "RouteId").toString();
+            String name = objectName.getKeyProperty("name");
+            name = ObjectName.unquote(name);
+
+            ManagedProcessorMBean managedProcessor = context.getExtension(ManagedCamelContext.class).getManagedProcessor(name);
+
+            if (managedProcessor != null) {
+                if (processorsForRoute.get(routeId) == null) {
+                    List<ManagedProcessorMBean> processorsList = new ArrayList<>();
+                    processorsList.add(managedProcessor);
+
+                    processorsForRoute.put(routeId, processorsList);
+                } else {
+                    processorsForRoute.get(routeId).add(managedProcessor);
+                }
+            }
+        }
+
+        // sort processors by position in route definition
+        for (Map.Entry<String, List<ManagedProcessorMBean>> entry : processorsForRoute.entrySet()) {
+            Collections.sort(entry.getValue(), Comparator.comparing(ManagedProcessorMBean::getIndex));
+        }
+
+        return processorsForRoute;
+    }
+
+    /**
+     * Gathers test details as xml
+     */
+    private String gatherTestDetailsAsXml() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<test>\n");
+        sb.append("  <class>").append(getClass().getName()).append("</class>\n");
+        sb.append("  <method>").append(currentTestName).append("</method>\n");
+        sb.append("  <time>").append(timeTaken()).append("</time>\n");
+        sb.append("</test>\n");
+        return sb.toString();
+    }
+
+    /**
+     * Returns the timeout to use when shutting down (unit in seconds).
+     * <p/>
+     * Will default use 10 seconds.
+     *
+     * @return the timeout to use
+     */
+    protected int getShutdownTimeout() {
+        return 10;
+    }
+
+    /**
+     * Whether or not JMX should be used during testing.
+     *
+     * @return <tt>false</tt> by default.
+     */
+    protected boolean useJmx() {
+        return false;
+    }
+
+    /**
+     * Whether or not type converters should be lazy loaded (notice core
+     * converters is always loaded)
+     *
+     * @return <tt>false</tt> by default.
+     */
+    @Deprecated
+    protected boolean isLazyLoadingTypeConverter() {
+        return false;
+    }
+
+    /**
+     * Override this method to include and override properties with the Camel
+     * {@link PropertiesComponent}.
+     *
+     * @return additional properties to add/override.
+     */
+    protected Properties useOverridePropertiesWithPropertiesComponent() {
+        return null;
+    }
+
+    /**
+     * Whether to ignore missing locations with the {@link PropertiesComponent}.
+     * For example when unit testing you may want to ignore locations that are
+     * not available in the environment used for testing.
+     *
+     * @return <tt>true</tt> to ignore, <tt>false</tt> to not ignore, and
+     *         <tt>null</tt> to leave as configured on the
+     *         {@link PropertiesComponent}
+     */
+    protected Boolean ignoreMissingLocationWithPropertiesComponent() {
+        return null;
+    }
+
+    protected void postProcessTest() throws Exception {
+        context = threadCamelContext.get();
+        template = threadTemplate.get();
+        fluentTemplate = threadFluentTemplate.get();
+        consumer = threadConsumer.get();
+        camelContextService = threadService.get();
+        applyCamelPostProcessor();
+    }
+
+    /**
+     * Applies the {@link CamelBeanPostProcessor} to this instance.
+     * <p>
+     * Derived classes using IoC / DI frameworks may wish to turn this into a
+     * NoOp such as for CDI we would just use CDI to inject this
+     */
+    protected void applyCamelPostProcessor() throws Exception {
+        // use the bean post processor if the test class is not dependency
+        // injected already by Spring Framework
+        boolean spring = hasClassAnnotation("org.springframework.boot.test.context.SpringBootTest", "org.springframework.context.annotation.ComponentScan");
+        if (!spring) {
+            context.getExtension(ExtendedCamelContext.class).getBeanPostProcessor().postProcessBeforeInitialization(this, getClass().getName());
+            context.getExtension(ExtendedCamelContext.class).getBeanPostProcessor().postProcessAfterInitialization(this, getClass().getName());
+        }
+    }
+
+    /**
+     * Does this test class have any of the following annotations on the
+     * class-level.
+     */
+    protected boolean hasClassAnnotation(String... names) {
+        for (String name : names) {
+            for (Annotation ann : getClass().getAnnotations()) {
+                String annName = ann.annotationType().getName();
+                if (annName.equals(name)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    protected void stopCamelContext() throws Exception {
+        doStopCamelContext(context, camelContextService);
+    }
+
+    private static void doStopCamelContext(CamelContext context, Service camelContextService) throws Exception {
+        if (camelContextService != null) {
+            if (camelContextService == threadService.get()) {
+                threadService.remove();
+            }
+            camelContextService.stop();
+        } else {
+            if (context != null) {
+                if (context == threadCamelContext.get()) {
+                    threadCamelContext.remove();
+                }
+                context.stop();
+            }
+        }
+    }
+
+    private static void doStopTemplates(ConsumerTemplate consumer, ProducerTemplate template, FluentProducerTemplate fluentTemplate) throws Exception {
+        if (consumer != null) {
+            if (consumer == threadConsumer.get()) {
+                threadConsumer.remove();
+            }
+            consumer.stop();
+        }
+        if (template != null) {
+            if (template == threadTemplate.get()) {
+                threadTemplate.remove();
+            }
+            template.stop();
+        }
+        if (fluentTemplate != null) {
+            if (fluentTemplate == threadFluentTemplate.get()) {
+                threadFluentTemplate.remove();
+            }
+            fluentTemplate.stop();
+        }
+    }
+
+    protected void startCamelContext() throws Exception {
+        if (camelContextService != null) {
+            camelContextService.start();
+        } else {
+            if (context instanceof DefaultCamelContext) {
+                DefaultCamelContext defaultCamelContext = (DefaultCamelContext)context;
+                if (!defaultCamelContext.isStarted()) {
+                    defaultCamelContext.start();
+                }
+            } else {
+                context.start();
+            }
+        }
+    }
+
+    protected CamelContext createCamelContext() throws Exception {
+        // for backwards compatibility
+        Registry registry = createRegistry();
+        if (registry instanceof FakeJndiRegistry) {
+            boolean inUse = ((FakeJndiRegistry)registry).isInUse();
+            if (!inUse) {
+                registry = null;
+            }
+        }
+        if (registry != null) {
+            String msg = "createRegistry() from camel-test is deprecated. Use createCamelRegistry if you want to control which registry to use, however"
+                         + " if you need to bind beans to the registry then this is possible already with the bind method on registry,"
+                         + " and there is no need to override this method.";
+            LOG.warn(msg);
+        } else {
+            registry = createCamelRegistry();
+        }
+
+        CamelContext context;
+        if (registry != null) {
+            context = new DefaultCamelContext(registry);
+        } else {
+            context = new DefaultCamelContext();
+        }
+        return context;
+    }
+
+    /**
+     * Allows to bind custom beans to the Camel {@link Registry}.
+     */
+    protected void bindToRegistry(Registry registry) throws Exception {
+        // noop
+    }
+
+    /**
+     * Override to use a custom {@link Registry}.
+     * <p>
+     * However if you need to bind beans to the registry then this is possible
+     * already with the bind method on registry, and there is no need to
+     * override this method.
+     */
+    protected Registry createCamelRegistry() throws Exception {
+        return null;
+    }
+
+    /**
+     * @deprecated use createCamelRegistry if you want to control which registry
+     *             to use, however if you need to bind beans to the registry
+     *             then this is possible already with the bind method on
+     *             registry, and there is no need to override this method.
+     */
+    @Deprecated
+    protected JndiRegistry createRegistry() throws Exception {
+        return new FakeJndiRegistry(createJndiContext());
+    }
+
+    /**
+     * @deprecated use createCamelRegistry if you want to control which registry
+     *             to use, however if you need to bind beans to the registry
+     *             then this is possible already with the bind method on
+     *             registry, and there is no need to use JndiRegistry and
+     *             override this method.
+     */
+    @Deprecated
+    protected Context createJndiContext() throws Exception {
+        Properties properties = new Properties();
+
+        // jndi.properties is optional
+        InputStream in = getClass().getClassLoader().getResourceAsStream("jndi.properties");
+        if (in != null) {
+            log.debug("Using jndi.properties from classpath root");
+            properties.load(in);
+        } else {
+            properties.put("java.naming.factory.initial", "org.apache.camel.support.jndi.CamelInitialContextFactory");
+        }
+        return new InitialContext(new Hashtable<>(properties));
+    }
+
+    private class FakeJndiRegistry extends JndiRegistry {
+
+        private boolean inUse;
+
+        public FakeJndiRegistry(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void bind(String name, Object object) {
+            super.bind(name, object);
+            inUse = true;
+        }
+
+        public boolean isInUse() {
+            // only if the end user bind beans then its in use
+            return inUse;
+        }
+    }
+
+    /**
+     * Factory method which derived classes can use to create a
+     * {@link RouteBuilder} to define the routes for testing
+     */
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                // no routes added by default
+            }
+        };
+    }
+
+    /**
+     * Factory method which derived classes can use to create an array of
+     * {@link org.apache.camel.builder.RouteBuilder}s to define the routes for
+     * testing
+     *
+     * @see #createRouteBuilder()
+     */
+    protected RoutesBuilder[] createRouteBuilders() throws Exception {
+        return new RoutesBuilder[] {createRouteBuilder()};
+    }
+
+    /**
+     * Resolves a mandatory endpoint for the given URI or an exception is thrown
+     *
+     * @param uri the Camel <a href="">URI</a> to use to create or resolve an
+     *            endpoint
+     * @return the endpoint
+     */
+    protected Endpoint resolveMandatoryEndpoint(String uri) {
+        return resolveMandatoryEndpoint(context, uri);
+    }
+
+    /**
+     * Resolves a mandatory endpoint for the given URI and expected type or an
+     * exception is thrown
+     *
+     * @param uri the Camel <a href="">URI</a> to use to create or resolve an
+     *            endpoint
+     * @return the endpoint
+     */
+    protected <T extends Endpoint> T resolveMandatoryEndpoint(String uri, Class<T> endpointType) {
+        return resolveMandatoryEndpoint(context, uri, endpointType);
+    }
+
+    /**
+     * Resolves the mandatory Mock endpoint using a URI of the form
+     * <code>mock:someName</code>
+     *
+     * @param uri the URI which typically starts with "mock:" and has some name
+     * @return the mandatory mock endpoint or an exception is thrown if it could
+     *         not be resolved
+     */
+    protected MockEndpoint getMockEndpoint(String uri) {
+        return getMockEndpoint(uri, true);
+    }
+
+    /**
+     * Resolves the {@link MockEndpoint} using a URI of the form
+     * <code>mock:someName</code>, optionally creating it if it does not exist.
+     * This implementation will lookup existing mock endpoints and match on the
+     * mock queue name, eg mock:foo and mock:foo?retainFirst=5 would match as
+     * the queue name is foo.
+     *
+     * @param uri the URI which typically starts with "mock:" and has some name
+     * @param create whether or not to allow the endpoint to be created if it
+     *            doesn't exist
+     * @return the mock endpoint or an {@link NoSuchEndpointException} is thrown
+     *         if it could not be resolved
+     * @throws NoSuchEndpointException is the mock endpoint does not exists
+     */
+    protected MockEndpoint getMockEndpoint(String uri, boolean create) throws NoSuchEndpointException {
+        // look for existing mock endpoints that have the same queue name, and to
+        // do that we need to normalize uri and strip out query parameters and
+        // whatnot
+        String n;
+        try {
+            n = URISupport.normalizeUri(uri);
+        } catch (Exception e) {
+            throw RuntimeCamelException.wrapRuntimeException(e);
+        }
+        // strip query
+        int idx = n.indexOf('?');
+        if (idx != -1) {
+            n = n.substring(0, idx);
+        }
+        final String target = n;
+
+        // lookup endpoints in registry and try to find it
+        MockEndpoint found = (MockEndpoint)context.getEndpointRegistry().values().stream().filter(e -> e instanceof MockEndpoint).filter(e -> {
+            String t = e.getEndpointUri();
+            // strip query
+            int idx2 = t.indexOf('?');
+            if (idx2 != -1) {
+                t = t.substring(0, idx2);
+            }
+            return t.equals(target);
+        }).findFirst().orElse(null);
+
+        if (found != null) {
+            return found;
+        }
+
+        if (create) {
+            return resolveMandatoryEndpoint(uri, MockEndpoint.class);
+        } else {
+            throw new NoSuchEndpointException(String.format("MockEndpoint %s does not exist.", uri));
+        }
+    }
+
+    /**
+     * Sends a message to the given endpoint URI with the body value
+     *
+     * @param endpointUri the URI of the endpoint to send to
+     * @param body the body for the message
+     */
+    protected void sendBody(String endpointUri, final Object body) {
+        template.send(endpointUri, new Processor() {
+            public void process(Exchange exchange) {
+                Message in = exchange.getIn();
+                in.setBody(body);
+            }
+        });
+    }
+
+    /**
+     * Sends a message to the given endpoint URI with the body value and
+     * specified headers
+     *
+     * @param endpointUri the URI of the endpoint to send to
+     * @param body the body for the message
+     * @param headers any headers to set on the message
+     */
+    protected void sendBody(String endpointUri, final Object body, final Map<String, Object> headers) {
+        template.send(endpointUri, new Processor() {
+            public void process(Exchange exchange) {
+                Message in = exchange.getIn();
+                in.setBody(body);
+                for (Map.Entry<String, Object> entry : headers.entrySet()) {
+                    in.setHeader(entry.getKey(), entry.getValue());
+                }
+            }
+        });
+    }
+
+    /**
+     * Sends messages to the given endpoint for each of the specified bodies
+     *
+     * @param endpointUri the endpoint URI to send to
+     * @param bodies the bodies to send, one per message
+     */
+    protected void sendBodies(String endpointUri, Object... bodies) {
+        for (Object body : bodies) {
+            sendBody(endpointUri, body);
+        }
+    }
+
+    /**
+     * Creates an exchange with the given body
+     */
+    protected Exchange createExchangeWithBody(Object body) {
+        return createExchangeWithBody(context, body);
+    }
+
+    /**
+     * Asserts that the given language name and expression evaluates to the
+     * given value on a specific exchange
+     */
+    protected void assertExpression(Exchange exchange, String languageName, String expressionText, Object expectedValue) {
+        Language language = assertResolveLanguage(languageName);
+
+        Expression expression = language.createExpression(expressionText);
+        assertNotNull(expression, "No Expression could be created for text: " + expressionText + " language: " + language);
+
+        assertExpression(expression, exchange, expectedValue);
+    }
+
+    /**
+     * Asserts that the given language name and predicate expression evaluates
+     * to the expected value on the message exchange
+     */
+    protected void assertPredicate(String languageName, String expressionText, Exchange exchange, boolean expected) {
+        Language language = assertResolveLanguage(languageName);
+
+        Predicate predicate = language.createPredicate(expressionText);
+        assertNotNull(predicate, "No Predicate could be created for text: " + expressionText + " language: " + language);
+
+        assertPredicate(predicate, exchange, expected);
+    }
+
+    /**
+     * Asserts that the language name can be resolved
+     */
+    protected Language assertResolveLanguage(String languageName) {
+        Language language = context.resolveLanguage(languageName);
+        assertNotNull(language, "No language found for name: " + languageName);
+        return language;
+    }
+
+    /**
+     * Asserts that all the expectations of the Mock endpoints are valid
+     */
+    protected void assertMockEndpointsSatisfied() throws InterruptedException {
+        MockEndpoint.assertIsSatisfied(context);
+    }
+
+    /**
+     * Asserts that all the expectations of the Mock endpoints are valid
+     */
+    protected void assertMockEndpointsSatisfied(long timeout, TimeUnit unit) throws InterruptedException {
+        MockEndpoint.assertIsSatisfied(context, timeout, unit);
+    }
+
+    /**
+     * Reset all Mock endpoints.
+     */
+    protected void resetMocks() {
+        MockEndpoint.resetMocks(context);
+    }
+
+    protected void assertValidContext(CamelContext context) {
+        assertNotNull(context, "No context found!");
+    }
+
+    protected <T extends Endpoint> T getMandatoryEndpoint(String uri, Class<T> type) {
+        T endpoint = context.getEndpoint(uri, type);
+        assertNotNull(endpoint, "No endpoint found for uri: " + uri);
+        return endpoint;
+    }
+
+    protected Endpoint getMandatoryEndpoint(String uri) {
+        Endpoint endpoint = context.getEndpoint(uri);
+        assertNotNull(endpoint, "No endpoint found for uri: " + uri);
+        return endpoint;
+    }
+
+    /**
+     * Disables the JMX agent. Must be called before the {@link #setUp()}
+     * method.
+     */
+    protected void disableJMX() {
+        System.setProperty(JmxSystemPropertyKeys.DISABLED, "true");
+    }
+
+    /**
+     * Enables the JMX agent. Must be called before the {@link #setUp()} method.
+     */
+    protected void enableJMX() {
+        System.setProperty(JmxSystemPropertyKeys.DISABLED, "false");
+    }
+
+    /**
+     * Single step debugs and Camel invokes this method before entering the
+     * given processor
+     */
+    protected void debugBefore(Exchange exchange, Processor processor, ProcessorDefinition<?> definition, String id, String label) {
+    }
+
+    /**
+     * Single step debugs and Camel invokes this method after processing the
+     * given processor
+     */
+    protected void debugAfter(Exchange exchange, Processor processor, ProcessorDefinition<?> definition, String id, String label, long timeTaken) {
+    }
+
+    /**
+     * To easily debug by overriding the <tt>debugBefore</tt> and
+     * <tt>debugAfter</tt> methods.
+     */
+    private class DebugBreakpoint extends BreakpointSupport {
+
+        @Override
+        public void beforeProcess(Exchange exchange, Processor processor, NamedNode definition) {
+            CamelTestSupport.this.debugBefore(exchange, processor, (ProcessorDefinition<?>)definition, definition.getId(), definition.getLabel());
+        }
+
+        @Override
+        public void afterProcess(Exchange exchange, Processor processor, NamedNode definition, long timeTaken) {
+            CamelTestSupport.this.debugAfter(exchange, processor, (ProcessorDefinition<?>)definition, definition.getId(), definition.getLabel(), timeTaken);
+        }
+    }
+
+}
diff --git a/components/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/TestSupport.java b/components/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/TestSupport.java
new file mode 100644
index 0000000..47d296f
--- /dev/null
+++ b/components/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/TestSupport.java
@@ -0,0 +1,543 @@
+/*
+ * 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.junit5;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.Message;
+import org.apache.camel.Predicate;
+import org.apache.camel.Route;
+import org.apache.camel.builder.Builder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.builder.ValueBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.support.PredicateAssertHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * A bunch of useful testing methods
+ */
+public abstract class TestSupport {
+
+    protected static final String LS = System.lineSeparator();
+    private static final Logger LOG = LoggerFactory.getLogger(TestSupport.class);
+    protected Logger log = LoggerFactory.getLogger(getClass());
+
+    // Builder methods for expressions used when testing
+    // -------------------------------------------------------------------------
+
+    /**
+     * Returns a value builder for the given header
+     */
+    public static ValueBuilder header(String name) {
+        return Builder.header(name);
+    }
+
+    /**
+     * Returns a value builder for the given exchange property
+     */
+    public static ValueBuilder exchangeProperty(String name) {
+        return Builder.exchangeProperty(name);
+    }
+
+    /**
+     * Returns a predicate and value builder for the inbound body on an exchange
+     */
+    public static ValueBuilder body() {
+        return Builder.body();
+    }
+
+    /**
+     * Returns a predicate and value builder for the inbound message body as a
+     * specific type
+     */
+    public static <T> ValueBuilder bodyAs(Class<T> type) {
+        return Builder.bodyAs(type);
+    }
+
+    /**
+     * Returns a value builder for the given system property
+     */
+    public static ValueBuilder systemProperty(String name) {
+        return Builder.systemProperty(name);
+    }
+
+    /**
+     * Returns a value builder for the given system property
+     */
+    public static ValueBuilder systemProperty(String name, String defaultValue) {
+        return Builder.systemProperty(name, defaultValue);
+    }
+
+    // Assertions
+    // -----------------------------------------------------------------------
+
+    public static <T> T assertIsInstanceOf(Class<T> expectedType, Object value) {
+        assertNotNull(value, "Expected an instance of type: " + expectedType.getName() + " but was null");
+        assertTrue(expectedType.isInstance(value), "Object should be of type " + expectedType.getName() + " but was: " + value + " with the type: " + value.getClass().getName());
+        return expectedType.cast(value);
+    }
+
+    public static void assertEndpointUri(Endpoint endpoint, String uri) {
+        assertNotNull(endpoint, "Endpoint is null when expecting endpoint for: " + uri);
+        assertEquals(endpoint.getEndpointUri(), "Endpoint uri for: " + endpoint, uri);
+    }
+
+    /**
+     * Asserts the In message on the exchange contains the expected value
+     */
+    public static Object assertInMessageHeader(Exchange exchange, String name, Object expected) {
+        return assertMessageHeader(exchange.getIn(), name, expected);
+    }
+
+    /**
+     * Asserts the Out message on the exchange contains the expected value
+     */
+    public static Object assertOutMessageHeader(Exchange exchange, String name, Object expected) {
+        return assertMessageHeader(exchange.getOut(), name, expected);
+    }
+
+    /**
+     * Asserts that the given exchange has an OUT message of the given body
+     * value
+     *
+     * @param exchange the exchange which should have an OUT message
+     * @param expected the expected value of the OUT message
+     * @throws InvalidPayloadException is thrown if the payload is not the
+     *             expected class type
+     */
+    public static void assertInMessageBodyEquals(Exchange exchange, Object expected) throws InvalidPayloadException {
+        assertNotNull(exchange, "Should have a response exchange!");
+
+        Object actual;
+        if (expected == null) {
+            actual = exchange.getIn().getMandatoryBody();
+            assertEquals(expected, actual, "in body of: " + exchange);
+        } else {
+            actual = exchange.getIn().getMandatoryBody(expected.getClass());
+        }
+        assertEquals(expected, actual, "in body of: " + exchange);
+
+        LOG.debug("Received response: " + exchange + " with in: " + exchange.getIn());
+    }
+
+    /**
+     * Asserts that the given exchange has an OUT message of the given body
+     * value
+     *
+     * @param exchange the exchange which should have an OUT message
+     * @param expected the expected value of the OUT message
+     * @throws InvalidPayloadException is thrown if the payload is not the
+     *             expected class type
+     */
+    public static void assertOutMessageBodyEquals(Exchange exchange, Object expected) throws InvalidPayloadException {
+        assertNotNull(exchange, "Should have a response exchange!");
+
+        Object actual;
+        if (expected == null) {
+            actual = exchange.getOut().getMandatoryBody();
+            assertEquals(expected, actual, "output body of: " + exchange);
+        } else {
+            actual = exchange.getOut().getMandatoryBody(expected.getClass());
+        }
+        assertEquals(expected, actual, "output body of: " + exchange);
+
+        LOG.debug("Received response: " + exchange + " with out: " + exchange.getOut());
+    }
+
+    public static Object assertMessageHeader(Message message, String name, Object expected) {
+        Object value = message.getHeader(name);
+        assertEquals(expected, value, "Header: " + name + " on Message: " + message);
+        return value;
+    }
+
+    /**
+     * Asserts that the given expression when evaluated returns the given answer
+     */
+    public static Object assertExpression(Expression expression, Exchange exchange, Object expected) {
+        Object value;
+        if (expected != null) {
+            value = expression.evaluate(exchange, expected.getClass());
+        } else {
+            value = expression.evaluate(exchange, Object.class);
+        }
+
+        LOG.debug("Evaluated expression: " + expression + " on exchange: " + exchange + " result: " + value);
+
+        assertEquals(expected, value, "Expression: " + expression + " on Exchange: " + exchange);
+        return value;
+    }
+
+    /**
+     * Asserts that the predicate returns the expected value on the exchange
+     */
+    public static void assertPredicateMatches(Predicate predicate, Exchange exchange) {
+        assertPredicate(predicate, exchange, true);
+    }
+
+    /**
+     * Asserts that the predicate returns the expected value on the exchange
+     */
+    public static void assertPredicateDoesNotMatch(Predicate predicate, Exchange exchange) {
+        try {
+            PredicateAssertHelper.assertMatches(predicate, "Predicate should match: ", exchange);
+        } catch (AssertionError e) {
+            LOG.debug("Caught expected assertion error: " + e);
+        }
+        assertPredicate(predicate, exchange, false);
+    }
+
+    /**
+     * Asserts that the predicate returns the expected value on the exchange
+     */
+    public static boolean assertPredicate(final Predicate predicate, Exchange exchange, boolean expected) {
+        if (expected) {
+            PredicateAssertHelper.assertMatches(predicate, "Predicate failed: ", exchange);
+        }
+        boolean value = predicate.matches(exchange);
+
+        LOG.debug("Evaluated predicate: " + predicate + " on exchange: " + exchange + " result: " + value);
+
+        assertEquals(expected, value, "Predicate: " + predicate + " on Exchange: " + exchange);
+        return value;
+    }
+
+    /**
+     * Resolves an endpoint and asserts that it is found
+     */
+    public static Endpoint resolveMandatoryEndpoint(CamelContext context, String uri) {
+        Endpoint endpoint = context.getEndpoint(uri);
+
+        assertNotNull(endpoint, "No endpoint found for URI: " + uri);
+
+        return endpoint;
+    }
+
+    /**
+     * Resolves an endpoint and asserts that it is found
+     */
+    public static <T extends Endpoint> T resolveMandatoryEndpoint(CamelContext context, String uri, Class<T> endpointType) {
+        T endpoint = context.getEndpoint(uri, endpointType);
+
+        assertNotNull(endpoint, "No endpoint found for URI: " + uri);
+
+        return endpoint;
+    }
+
+    /**
+     * Creates an exchange with the given body
+     */
+    protected Exchange createExchangeWithBody(CamelContext camelContext, Object body) {
+        Exchange exchange = new DefaultExchange(camelContext);
+        Message message = exchange.getIn();
+        message.setHeader("testClass", getClass().getName());
+        message.setBody(body);
+        return exchange;
+    }
+
+    public static <T> T assertOneElement(List<T> list) {
+        assertEquals(1, list.size(), "Size of list should be 1: " + list);
+        return list.get(0);
+    }
+
+    /**
+     * Asserts that a list is of the given size
+     */
+    public static <T> List<T> assertListSize(List<T> list, int size) {
+        return assertListSize("List", list, size);
+    }
+
+    /**
+     * Asserts that a list is of the given size
+     */
+    public static <T> List<T> assertListSize(String message, List<T> list, int size) {
+        assertEquals(size, list.size(), message + " should be of size: " + size + " but is: " + list);
+        return list;
+    }
+
+    /**
+     * Asserts that a list is of the given size
+     */
+    public static <T> Collection<T> assertCollectionSize(Collection<T> list, int size) {
+        return assertCollectionSize("List", list, size);
+    }
+
+    /**
+     * Asserts that a list is of the given size
+     */
+    public static <T> Collection<T> assertCollectionSize(String message, Collection<T> list, int size) {
+        assertEquals(size, list.size(), message + " should be of size: " + size + " but is: " + list);
+        return list;
+    }
+
+    /**
+     * A helper method to create a list of Route objects for a given route
+     * builder
+     */
+    public static List<Route> getRouteList(RouteBuilder builder) throws Exception {
+        CamelContext context = new DefaultCamelContext();
+        context.addRoutes(builder);
+        context.start();
+        List<Route> answer = context.getRoutes();
+        context.stop();
+        return answer;
+    }
+
+    /**
+     * Asserts that the text contains the given string
+     *
+     * @param text the text to compare
+     * @param containedText the text which must be contained inside the other
+     *            text parameter
+     */
+    public static void assertStringContains(String text, String containedText) {
+        assertNotNull(text, "Text should not be null!");
+        assertTrue(text.contains(containedText), "Text: " + text + " does not contain: " + containedText);
+    }
+
+    /**
+     * Recursively delete a directory, useful to zapping test data
+     *
+     * @param file the directory to be deleted
+     * @return <tt>false</tt> if error deleting directory
+     */
+    public static boolean deleteDirectory(String file) {
+        return deleteDirectory(new File(file));
+    }
+
+    /**
+     * Recursively delete a directory, useful to zapping test data
+     *
+     * @param file the directory to be deleted
+     * @return <tt>false</tt> if error deleting directory
+     */
+    public static boolean deleteDirectory(File file) {
+        int tries = 0;
+        int maxTries = 5;
+        boolean exists = true;
+        while (exists && (tries < maxTries)) {
+            recursivelyDeleteDirectory(file);
+            tries++;
+            exists = file.exists();
+            if (exists) {
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+            }
+        }
+        return !exists;
+    }
+
+    private static void recursivelyDeleteDirectory(File file) {
+        if (!file.exists()) {
+            return;
+        }
+
+        if (file.isDirectory()) {
+            File[] files = file.listFiles();
+            for (File child : files) {
+                recursivelyDeleteDirectory(child);
+            }
+        }
+        boolean success = file.delete();
+        if (!success) {
+            LOG.warn("Deletion of file: " + file.getAbsolutePath() + " failed");
+        }
+    }
+
+    /**
+     * create the directory
+     *
+     * @param file the directory to be created
+     */
+    public static void createDirectory(String file) {
+        File dir = new File(file);
+        dir.mkdirs();
+    }
+
+    /**
+     * To be used for folder/directory comparison that works across different
+     * platforms such as Window, Mac and Linux.
+     */
+    public static void assertDirectoryEquals(String expected, String actual) {
+        assertDirectoryEquals(null, expected, actual);
+    }
+
+    /**
+     * To be used for folder/directory comparison that works across different
+     * platforms such as Window, Mac and Linux.
+     */
+    public static void assertDirectoryEquals(String message, String expected, String actual) {
+        // must use single / as path separators
+        String expectedPath = expected.replace('\\', '/');
+        String actualPath = actual.replace('\\', '/');
+
+        if (message != null) {
+            assertEquals(message, expectedPath, actualPath);
+        } else {
+            assertEquals(expectedPath, actualPath);
+        }
+    }
+
+    /**
+     * To be used to check is a directory is found in the file system
+     */
+    public static void assertDirectoryExists(String filename) {
+        File file = new File(filename);
+        assertTrue(file.exists(), "Directory " + filename + " should exist");
+        assertTrue(file.isDirectory(), "Directory " + filename + " should be a directory");
+    }
+
+    /**
+     * To be used to check is a file is found in the file system
+     */
+    public static void assertFileExists(String filename) {
+        File file = new File(filename);
+        assertTrue(file.exists(), "File " + filename + " should exist");
+        assertTrue(file.isFile(), "File " + filename + " should be a file");
+    }
+
+    /**
+     * To be used to check is a file is <b>not</b> found in the file system
+     */
+    public static void assertFileNotExists(String filename) {
+        File file = new File(filename);
+        assertFalse(file.exists(), "File " + filename + " should not exist");
+    }
+
+    /**
+     * Is this OS the given platform.
+     * <p/>
+     * Uses <tt>os.name</tt> from the system properties to determine the OS.
+     *
+     * @param platform such as Windows
+     * @return <tt>true</tt> if its that platform.
+     */
+    public static boolean isPlatform(String platform) {
+        String osName = System.getProperty("os.name").toLowerCase(Locale.US);
+        return osName.contains(platform.toLowerCase(Locale.US));
+    }
+
+    /**
+     * Is this Java by the given vendor.
+     * <p/>
+     * Uses <tt>java.vendor</tt> from the system properties to determine the
+     * vendor.
+     *
+     * @param vendor such as IBM
+     * @return <tt>true</tt> if its that vendor.
+     */
+    public static boolean isJavaVendor(String vendor) {
+        String javaVendor = System.getProperty("java.vendor").toLowerCase(Locale.US);
+        return javaVendor.contains(vendor.toLowerCase(Locale.US));
+    }
+
+    /**
+     * Is this Java 1.5
+     *
+     * @return <tt>true</tt> if its Java 1.5, <tt>false</tt> if its not (for
+     *         example Java 1.6 or better)
+     * @deprecated will be removed in the future as Camel requires JDK1.8+
+     */
+    @Deprecated
+    public static boolean isJava15() {
+        return getJavaMajorVersion() == 5;
+    }
+
+    /**
+     * Is this Java 1.6
+     *
+     * @return <tt>true</tt> if its Java 1.6, <tt>false</tt> if its not (for
+     *         example Java 1.7 or better)
+     * @deprecated will be removed in the future as Camel requires JDK1.8+
+     */
+    @Deprecated
+    public static boolean isJava16() {
+        return getJavaMajorVersion() == 6;
+
+    }
+
+    /**
+     * Is this Java 1.7
+     *
+     * @return <tt>true</tt> if its Java 1.7, <tt>false</tt> if its not (for
+     *         example Java 1.6 or older)
+     * @deprecated will be removed in the future as Camel requires JDK1.8+
+     */
+    @Deprecated
+    public static boolean isJava17() {
+        return getJavaMajorVersion() == 7;
+
+    }
+
+    /**
+     * Is this Java 1.8
+     *
+     * @return <tt>true</tt> if its Java 1.8, <tt>false</tt> if its not (for
+     *         example Java 1.7 or older)
+     */
+    public static boolean isJava18() {
+        return getJavaMajorVersion() == 8;
+
+    }
+
+    /**
+     * Is this Java 1.9
+     *
+     * @return <tt>true</tt> if its Java 1.9, <tt>false</tt> if its not (for
+     *         example Java 1.8 or older)
+     */
+    public static boolean isJava19() {
+        return getJavaMajorVersion() == 9;
+
+    }
+
+    /**
+     * Returns the current major Java version e.g 8.
+     * <p/>
+     * Uses <tt>java.specification.version</tt> from the system properties to
+     * determine the major version.
+     * 
+     * @return the current major Java version.
+     */
+    public static int getJavaMajorVersion() {
+        String javaSpecVersion = System.getProperty("java.specification.version");
+        if (javaSpecVersion.contains(".")) { // before jdk 9
+            return Integer.parseInt(javaSpecVersion.split("\\.")[1]);
+        } else {
+            return Integer.parseInt(javaSpecVersion);
+        }
+    }
+
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/AvailablePortFinderTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/AvailablePortFinderTest.java
new file mode 100644
index 0000000..365831d
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/AvailablePortFinderTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+import java.net.ServerSocket;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class AvailablePortFinderTest {
+
+    @Test
+    public void getNextAvailablePort() throws Exception {
+        int p1 = AvailablePortFinder.getNextAvailable();
+        int p2 = AvailablePortFinder.getNextAvailable();
+        assertFalse(p1 == p2, "Port " + p1 + " Port2 " + p2);
+    }
+
+    @Test
+    public void testGetNextAvailablePortInt() throws Exception {
+        int p1 = AvailablePortFinder.getNextAvailable(9123);
+        int p2 = AvailablePortFinder.getNextAvailable(9123);
+        // these calls only check availability but don't mark the port as in
+        // use.
+        assertEquals(p1, p2);
+    }
+
+    @Test
+    public void testNotAvailablePort() throws Exception {
+        int p1 = AvailablePortFinder.getNextAvailable(11000);
+        ServerSocket socket = new ServerSocket(p1);
+        int p2 = AvailablePortFinder.getNextAvailable(p1);
+        assertFalse(p1 == p2, "Port " + p1 + " Port2 " + p2);
+        socket.close();
+    }
+
+    @Test
+    public void getMinOutOfRangePort() throws Exception {
+        assertThrows(IllegalArgumentException.class, () -> {
+            AvailablePortFinder.getNextAvailable(AvailablePortFinder.MIN_PORT_NUMBER - 1);
+        });
+    }
+
+    @Test
+    public void getMaxOutOfRangePort() throws Exception {
+        assertThrows(IllegalArgumentException.class, () -> {
+            AvailablePortFinder.getNextAvailable(AvailablePortFinder.MAX_PORT_NUMBER + 1);
+        });
+    }
+
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/CamelTestSupportTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/CamelTestSupportTest.java
new file mode 100644
index 0000000..b2aec5e
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/CamelTestSupportTest.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.junit5;
+
+import org.apache.camel.NoSuchEndpointException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class CamelTestSupportTest extends CamelTestSupport {
+
+    @Override
+    @BeforeEach
+    public void setUp() throws Exception {
+        replaceRouteFromWith("routeId", "direct:start");
+        super.setUp();
+    }
+
+    @Test
+    public void replacesFromEndpoint() throws Exception {
+        getMockEndpoint("mock:result").expectedBodiesReceived("Hello World");
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void exceptionThrownWhenEndpointNotFoundAndNoCreate() {
+        assertThrows(NoSuchEndpointException.class, () -> {
+            getMockEndpoint("mock:bogus", false);
+        });
+    }
+
+    @Test
+    public void exceptionThrownWhenEndpointNotAMockEndpoint() {
+        assertThrows(NoSuchEndpointException.class, () -> {
+            getMockEndpoint("direct:something", false);
+        });
+    }
+
+    @Test
+    public void autoCreateNonExisting() {
+        MockEndpoint mock = getMockEndpoint("mock:bogus2", true);
+        assertNotNull(mock);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:something").id("routeId").to("mock:result");
+            }
+        };
+    }
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternExcludeTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternExcludeTest.java
new file mode 100644
index 0000000..43af8f4
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternExcludeTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.junit5;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class RouteFilterPatternExcludeTest extends CamelTestSupport {
+
+    @Override
+    public String getRouteFilterExcludePattern() {
+        return "bar*";
+    }
+
+    @Test
+    public void testRouteFilter() throws Exception {
+        assertEquals(1, context.getRoutes().size());
+        assertEquals(1, context.getRouteDefinitions().size());
+        assertEquals("foo", context.getRouteDefinitions().get(0).getId());
+
+        getMockEndpoint("mock:foo").expectedMessageCount(1);
+
+        template.sendBody("direct:foo", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:foo").routeId("foo").to("mock:foo");
+
+                from("direct:bar").routeId("bar").to("mock:bar");
+            }
+        };
+    }
+
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeExcludeTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeExcludeTest.java
new file mode 100644
index 0000000..426a1cb
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeExcludeTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.junit5;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class RouteFilterPatternIncludeExcludeTest extends CamelTestSupport {
+
+    @Override
+    public String getRouteFilterIncludePattern() {
+        return "foo*";
+    }
+
+    @Override
+    public String getRouteFilterExcludePattern() {
+        return "jms:*";
+    }
+
+    @Test
+    public void testRouteFilter() throws Exception {
+        assertEquals(1, context.getRoutes().size());
+        assertEquals(1, context.getRouteDefinitions().size());
+        assertEquals("foo", context.getRouteDefinitions().get(0).getId());
+
+        getMockEndpoint("mock:foo").expectedMessageCount(1);
+
+        template.sendBody("direct:foo", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:foo").routeId("foo").to("mock:foo");
+
+                from("direct:bar").routeId("bar").to("mock:bar");
+
+                from("jms:beer").routeId("foolish").to("mock:beer");
+            }
+        };
+    }
+
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeTest.java
new file mode 100644
index 0000000..a724e3a
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.junit5;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class RouteFilterPatternIncludeTest extends CamelTestSupport {
+
+    @Override
+    public String getRouteFilterIncludePattern() {
+        return "foo*";
+    }
+
+    @Test
+    public void testRouteFilter() throws Exception {
+        assertEquals(1, context.getRoutes().size());
+        assertEquals(1, context.getRouteDefinitions().size());
+        assertEquals("foo", context.getRouteDefinitions().get(0).getId());
+
+        getMockEndpoint("mock:foo").expectedMessageCount(1);
+
+        template.sendBody("direct:foo", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:foo").routeId("foo").to("mock:foo");
+
+                from("direct:bar").routeId("bar").to("mock:bar");
+            }
+        };
+    }
+
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AdviceWithLambdaTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AdviceWithLambdaTest.java
new file mode 100644
index 0000000..3ce739f
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AdviceWithLambdaTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.AdviceWithRouteBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class AdviceWithLambdaTest extends CamelTestSupport {
+
+    @Override
+    public boolean isUseAdviceWith() {
+        return true;
+    }
+
+    @Test
+    public void testAdviceWith() throws Exception {
+        getMockEndpoint("mock:result").expectedMessageCount(1);
+
+        // advice the route in one line
+        AdviceWithRouteBuilder.adviceWith(context, "foo", a -> a.weaveAddLast().to("mock:result"));
+
+        // start Camel
+        context.start();
+
+        template.sendBody("direct:start", "Bye World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start").routeId("foo").to("log:foo");
+            }
+        };
+    }
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AdviceWithNotStartedTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AdviceWithNotStartedTest.java
new file mode 100644
index 0000000..68aae9c
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AdviceWithNotStartedTest.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.junit5.patterns;
+
+import java.util.concurrent.RejectedExecutionException;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.AdviceWithRouteBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.reifier.RouteReifier;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class AdviceWithNotStartedTest extends CamelTestSupport {
+
+    @Override
+    public boolean isUseAdviceWith() {
+        return true;
+    }
+
+    @Test
+    public void testNotStarted() throws Exception {
+        RouteReifier.adviceWith(context.getRouteDefinition("foo"), context, new AdviceWithRouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                weaveAddLast().to("mock:result");
+            }
+        });
+
+        getMockEndpoint("mock:result").expectedMessageCount(1);
+
+        try {
+            template.sendBody("direct:start", "Hello World");
+            fail("Should throw exception");
+        } catch (CamelExecutionException e) {
+            assertIsInstanceOf(RejectedExecutionException.class, e.getCause());
+        }
+
+        // start Camel
+        context.start();
+
+        template.sendBody("direct:start", "Bye World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start").routeId("foo").to("log:foo");
+            }
+        };
+    }
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AsyncSendMockTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AsyncSendMockTest.java
new file mode 100644
index 0000000..f1f8ca9
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AsyncSendMockTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class AsyncSendMockTest extends CamelTestSupport {
+    @Override
+    public String isMockEndpoints() {
+        return "seda*";
+    }
+
+    @Test
+    public void testmakeAsyncApiCall() {
+        try {
+            getMockEndpoint("mock:seda:start").expectedHeaderReceived("username", "admin123");
+            getMockEndpoint("mock:seda:start").expectedBodiesReceived("Hello");
+            DefaultExchange dfex = new DefaultExchange(context);
+            dfex.getIn().setHeader("username", "admin123");
+            dfex.getIn().setHeader("password", "admin");
+            dfex.getIn().setBody("Hello");
+            template.asyncSend("seda:start", dfex);
+            assertMockEndpointsSatisfied();
+        } catch (Exception e) {
+            e.printStackTrace();
+            assertTrue(false, "Failed to make async call to api");
+        }
+    }
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/CreateCamelContextPerTestFalseTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/CreateCamelContextPerTestFalseTest.java
new file mode 100644
index 0000000..ed769d9
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/CreateCamelContextPerTestFalseTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.junit5.patterns;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class CreateCamelContextPerTestFalseTest extends CamelTestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CreateCamelContextPerTestFalseTest.class);
+
+    private static final AtomicInteger CREATED_CONTEXTS = new AtomicInteger();
+    private static final AtomicInteger POST_TEAR_DOWN = new AtomicInteger();
+
+    @EndpointInject("mock:result")
+    protected MockEndpoint resultEndpoint;
+
+    @Produce("direct:start")
+    protected ProducerTemplate template;
+
+    @Override
+    public boolean isCreateCamelContextPerClass() {
+        return false;
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        LOG.info("createCamelContext()");
+        CREATED_CONTEXTS.incrementAndGet();
+        return super.createCamelContext();
+    }
+
+    @Override
+    protected void doPostTearDown() throws Exception {
+        LOG.info("doPostTearDown()");
+        POST_TEAR_DOWN.incrementAndGet();
+        super.doPostTearDown();
+    }
+
+    @Test
+    public void testSendMatchingMessage() throws Exception {
+        String expectedBody = "<matched/>";
+
+        resultEndpoint.expectedBodiesReceived(expectedBody);
+
+        template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+        resultEndpoint.assertIsSatisfied();
+
+        assertTrue(CREATED_CONTEXTS.get() >= 1, "Should create 1 or more CamelContext per test class");
+    }
+
+    @Test
+    public void testSendAnotherMatchingMessage() throws Exception {
+        String expectedBody = "<matched/>";
+
+        resultEndpoint.expectedBodiesReceived(expectedBody);
+
+        template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+        resultEndpoint.assertIsSatisfied();
+
+        assertTrue(CREATED_CONTEXTS.get() >= 1, "Should create 1 or more CamelContext per test class");
+    }
+
+    @Test
+    public void testSendNotMatchingMessage() throws Exception {
+        resultEndpoint.expectedMessageCount(0);
+
+        template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
+
+        resultEndpoint.assertIsSatisfied();
+
+        assertTrue(CREATED_CONTEXTS.get() >= 1, "Should create 1 or more CamelContext per test class");
+    }
+
+    @AfterAll
+    public static void validateTearDown() throws Exception {
+        assertEquals(3, CREATED_CONTEXTS.get());
+        assertEquals(3, POST_TEAR_DOWN.get());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+            }
+        };
+    }
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/CreateCamelContextPerTestTrueTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/CreateCamelContextPerTestTrueTest.java
new file mode 100644
index 0000000..41f5e79
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/CreateCamelContextPerTestTrueTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.junit5.patterns;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.apache.camel.util.StopWatch;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class CreateCamelContextPerTestTrueTest extends CamelTestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CreateCamelContextPerTestTrueTest.class);
+
+    private static final AtomicInteger CREATED_CONTEXTS = new AtomicInteger();
+    private static final AtomicInteger POST_TEAR_DOWN = new AtomicInteger();
+    private static final CountDownLatch LATCH = new CountDownLatch(1);
+
+    @EndpointInject("mock:result")
+    protected MockEndpoint resultEndpoint;
+
+    @Produce("direct:start")
+    protected ProducerTemplate template;
+
+    @Override
+    public boolean isCreateCamelContextPerClass() {
+        return true;
+    }
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        LOG.info("createCamelContext()");
+        CREATED_CONTEXTS.incrementAndGet();
+        return super.createCamelContext();
+    }
+
+    @Override
+    protected void doPostTearDown() throws Exception {
+        LOG.info("doPostTearDown()");
+        POST_TEAR_DOWN.incrementAndGet();
+        super.doPostTearDown();
+        LATCH.await(5, TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void testSendMatchingMessage() throws Exception {
+        String expectedBody = "<matched/>";
+
+        resultEndpoint.expectedBodiesReceived(expectedBody);
+
+        template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+        resultEndpoint.assertIsSatisfied();
+
+        assertEquals(1, CREATED_CONTEXTS.get(), "Should only create 1 CamelContext per test class");
+        assertEquals(0, POST_TEAR_DOWN.get(), "Should not call postTearDown yet");
+    }
+
+    @Test
+    public void testSendAnotherMatchingMessage() throws Exception {
+        String expectedBody = "<matched/>";
+
+        resultEndpoint.expectedBodiesReceived(expectedBody);
+
+        template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+        resultEndpoint.assertIsSatisfied();
+
+        assertEquals(1, CREATED_CONTEXTS.get(), "Should only create 1 CamelContext per test class");
+        assertEquals(0, POST_TEAR_DOWN.get(), "Should not call postTearDown yet");
+    }
+
+    @Test
+    public void testSendNotMatchingMessage() throws Exception {
+        resultEndpoint.expectedMessageCount(0);
+
+        template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
+
+        resultEndpoint.assertIsSatisfied();
+
+        assertEquals(1, CREATED_CONTEXTS.get(), "Should only create 1 CamelContext per test class");
+        assertEquals(0, POST_TEAR_DOWN.get(), "Should not call postTearDown yet");
+    }
+
+    @AfterAll
+    public static void shouldTearDown() throws Exception {
+        // we are called before doPostTearDown so lets wait for that to be
+        // called
+        Runnable r = () -> {
+            try {
+                StopWatch watch = new StopWatch();
+                while (watch.taken() < 5000) {
+                    LOG.debug("Checking for tear down called correctly");
+                    if (POST_TEAR_DOWN.get() == 1) {
+                        LATCH.countDown();
+                        break;
+                    } else {
+                        try {
+                            Thread.sleep(100);
+                        } catch (InterruptedException e) {
+                            break;
+                        }
+                    }
+                }
+            } finally {
+                LOG.info("Should only call postTearDown 1 time per test class, called: " + POST_TEAR_DOWN.get());
+                assertEquals(1, POST_TEAR_DOWN.get(), "Should only call postTearDown 1 time per test class");
+            }
+        };
+        Thread t = new Thread(r);
+        t.setDaemon(false);
+        t.setName("shouldTearDown checker");
+        t.start();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+            }
+        };
+    }
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugJUnit5Test.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugJUnit5Test.java
new file mode 100644
index 0000000..9a6811b
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugJUnit5Test.java
@@ -0,0 +1,84 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class DebugJUnit5Test extends CamelTestSupport {
+
+    // START SNIPPET: e1
+    @Override
+    public boolean isUseDebugger() {
+        // must enable debugger
+        return true;
+    }
+
+    @Override
+    protected void debugBefore(Exchange exchange, Processor processor, ProcessorDefinition<?> definition, String id, String shortName) {
+        // this method is invoked before we are about to enter the given
+        // processor
+        // from your Java editor you can just add a breakpoint in the code line
+        // below
+        log.info("Before " + definition + " with body " + exchange.getIn().getBody());
+    }
+    // END SNIPPET: e1
+
+    @Test
+    public void testDebugger() throws Exception {
+        // set mock expectations
+        getMockEndpoint("mock:a").expectedMessageCount(1);
+        getMockEndpoint("mock:b").expectedMessageCount(1);
+
+        // send a message
+        template.sendBody("direct:start", "World");
+
+        // assert mocks
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testTwo() throws Exception {
+        // set mock expectations
+        getMockEndpoint("mock:a").expectedMessageCount(2);
+        getMockEndpoint("mock:b").expectedMessageCount(2);
+
+        // send a message
+        template.sendBody("direct:start", "World");
+        template.sendBody("direct:start", "Camel");
+
+        // assert mocks
+        assertMockEndpointsSatisfied();
+    }
+
+    // START SNIPPET: e2
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // this is the route we want to debug
+                from("direct:start").to("mock:a").transform(body().prepend("Hello ")).to("mock:b");
+            }
+        };
+    }
+    // END SNIPPET: e2
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugNoLazyTypeConverterTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugNoLazyTypeConverterTest.java
new file mode 100644
index 0000000..7584f15
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugNoLazyTypeConverterTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class DebugNoLazyTypeConverterTest extends CamelTestSupport {
+
+    @Override
+    protected boolean isLazyLoadingTypeConverter() {
+        return false;
+    }
+
+    // START SNIPPET: e1
+    @Override
+    public boolean isUseDebugger() {
+        // must enable debugger
+        return true;
+    }
+
+    @Override
+    protected void debugBefore(Exchange exchange, Processor processor, ProcessorDefinition<?> definition, String id, String shortName) {
+        // this method is invoked before we are about to enter the given
+        // processor
+        // from your Java editor you can just add a breakpoint in the code line
+        // below
+        log.info("Before " + definition + " with body " + exchange.getIn().getBody());
+    }
+    // END SNIPPET: e1
+
+    @Test
+    public void testDebugger() throws Exception {
+        // set mock expectations
+        getMockEndpoint("mock:a").expectedMessageCount(1);
+        getMockEndpoint("mock:b").expectedMessageCount(1);
+
+        // send a message
+        template.sendBody("direct:start", "World");
+
+        // assert mocks
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testTwo() throws Exception {
+        // set mock expectations
+        getMockEndpoint("mock:a").expectedMessageCount(2);
+        getMockEndpoint("mock:b").expectedMessageCount(2);
+
+        // send a message
+        template.sendBody("direct:start", "World");
+        template.sendBody("direct:start", "Camel");
+
+        // assert mocks
+        assertMockEndpointsSatisfied();
+    }
+
+    // START SNIPPET: e2
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // this is the route we want to debug
+                from("direct:start").to("mock:a").transform(body().prepend("Hello ")).to("mock:b");
+            }
+        };
+    }
+    // END SNIPPET: e2
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugTest.java
new file mode 100644
index 0000000..38f75d5
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class DebugTest extends CamelTestSupport {
+
+    // START SNIPPET: e1
+    @Override
+    public boolean isUseDebugger() {
+        // must enable debugger
+        return true;
+    }
+
+    @Override
+    protected void debugBefore(Exchange exchange, Processor processor, ProcessorDefinition<?> definition, String id, String shortName) {
+        // this method is invoked before we are about to enter the given
+        // processor
+        // from your Java editor you can just add a breakpoint in the code line
+        // below
+        log.info("Before " + definition + " with body " + exchange.getIn().getBody());
+    }
+    // END SNIPPET: e1
+
+    @Test
+    public void testDebugger() throws Exception {
+        // set mock expectations
+        getMockEndpoint("mock:a").expectedMessageCount(1);
+        getMockEndpoint("mock:b").expectedMessageCount(1);
+
+        // send a message
+        template.sendBody("direct:start", "World");
+
+        // assert mocks
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testTwo() throws Exception {
+        // set mock expectations
+        getMockEndpoint("mock:a").expectedMessageCount(2);
+        getMockEndpoint("mock:b").expectedMessageCount(2);
+
+        // send a message
+        template.sendBody("direct:start", "World");
+        template.sendBody("direct:start", "Camel");
+
+        // assert mocks
+        assertMockEndpointsSatisfied();
+    }
+
+    // START SNIPPET: e2
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // this is the route we want to debug
+                from("direct:start").to("mock:a").transform(body().prepend("Hello ")).to("mock:b");
+            }
+        };
+    }
+    // END SNIPPET: e2
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterCreateCamelContextPerClassTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterCreateCamelContextPerClassTest.java
new file mode 100644
index 0000000..0f5be29
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterCreateCamelContextPerClassTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests filtering using Camel Test
+ */
+// START SNIPPET: example
+public class FilterCreateCamelContextPerClassTest extends CamelTestSupport {
+
+    @Override
+    public boolean isCreateCamelContextPerClass() {
+        // we override this method and return true, to tell Camel test-kit that
+        // it should only create CamelContext once (per class), so we will
+        // re-use the CamelContext between each test method in this class
+        return true;
+    }
+
+    @Test
+    public void testSendMatchingMessage() throws Exception {
+        String expectedBody = "<matched/>";
+
+        getMockEndpoint("mock:result").expectedBodiesReceived(expectedBody);
+
+        template.sendBodyAndHeader("direct:start", expectedBody, "foo", "bar");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    public void testSendNotMatchingMessage() throws Exception {
+        getMockEndpoint("mock:result").expectedMessageCount(0);
+
+        template.sendBodyAndHeader("direct:start", "<notMatched/>", "foo", "notMatchedHeaderValue");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+            }
+        };
+    }
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterFluentTemplateTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterFluentTemplateTest.java
new file mode 100644
index 0000000..e4cef04
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterFluentTemplateTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.FluentProducerTemplate;
+import org.apache.camel.Produce;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests filtering using Camel Test
+ */
+// START SNIPPET: example
+// tag::example[]
+public class FilterFluentTemplateTest extends CamelTestSupport {
+
+    @EndpointInject("mock:result")
+    protected MockEndpoint resultEndpoint;
+
+    @Produce("direct:start")
+    protected FluentProducerTemplate fluentTemplate;
+
+    @Override
+    public boolean isDumpRouteCoverage() {
+        return true;
+    }
+
+    @Test
+    public void testSendMatchingMessage() throws Exception {
+        String expectedBody = "<matched/>";
+
+        resultEndpoint.expectedBodiesReceived(expectedBody);
+
+        fluentTemplate.withBody(expectedBody).withHeader("foo", "bar").send();
+
+        resultEndpoint.assertIsSatisfied();
+    }
+
+    @Test
+    public void testSendNotMatchingMessage() throws Exception {
+        resultEndpoint.expectedMessageCount(0);
+
+        fluentTemplate.withBody("<notMatched/>").withHeader("foo", "notMatchedHeaderValue").send();
+
+        resultEndpoint.assertIsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+            }
+        };
+    }
+}
+// end::example[]
+// END SNIPPET: example
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterJUnit5Test.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterJUnit5Test.java
new file mode 100644
index 0000000..e946d6b
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterJUnit5Test.java
@@ -0,0 +1,69 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests filtering using Camel Test
+ */
+// START SNIPPET: example
+public class FilterJUnit5Test extends CamelTestSupport {
+
+    @EndpointInject("mock:result")
+    protected MockEndpoint resultEndpoint;
+
+    @Produce("direct:start")
+    protected ProducerTemplate template;
+
+    @Test
+    public void testSendMatchingMessage() throws Exception {
+        String expectedBody = "<matched/>";
+
+        resultEndpoint.expectedBodiesReceived(expectedBody);
+
+        template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+        resultEndpoint.assertIsSatisfied();
+    }
+
+    @Test
+    public void testSendNotMatchingMessage() throws Exception {
+        resultEndpoint.expectedMessageCount(0);
+
+        template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
+
+        resultEndpoint.assertIsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+            }
+        };
+    }
+
+}
+// END SNIPPET: example
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterTest.java
new file mode 100644
index 0000000..a2455ce
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests filtering using Camel Test
+ */
+// START SNIPPET: example
+// tag::example[]
+public class FilterTest extends CamelTestSupport {
+
+    @EndpointInject("mock:result")
+    protected MockEndpoint resultEndpoint;
+
+    @Produce("direct:start")
+    protected ProducerTemplate template;
+
+    @Override
+    public boolean isDumpRouteCoverage() {
+        return true;
+    }
+
+    @Test
+    public void testSendMatchingMessage() throws Exception {
+        String expectedBody = "<matched/>";
+
+        resultEndpoint.expectedBodiesReceived(expectedBody);
+
+        template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+        resultEndpoint.assertIsSatisfied();
+    }
+
+    @Test
+    public void testSendNotMatchingMessage() throws Exception {
+        resultEndpoint.expectedMessageCount(0);
+
+        template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
+
+        resultEndpoint.assertIsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+            }
+        };
+    }
+}
+// end::example[]
+// END SNIPPET: example
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/GetMockEndpointTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/GetMockEndpointTest.java
new file mode 100644
index 0000000..8edec28
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/GetMockEndpointTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class GetMockEndpointTest extends CamelTestSupport {
+
+    @Test
+    public void testMock() throws Exception {
+        getMockEndpoint("mock:result").expectedBodiesReceived("Hello World");
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start").to("mock:result?failFast=false");
+            }
+        };
+    }
+
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsAndSkipJUnit5Test.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsAndSkipJUnit5Test.java
new file mode 100644
index 0000000..f2a487e
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsAndSkipJUnit5Test.java
@@ -0,0 +1,68 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.seda.SedaEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+// START SNIPPET: e1
+// tag::e1[]
+public class IsMockEndpointsAndSkipJUnit5Test extends CamelTestSupport {
+
+    @Override
+    public String isMockEndpointsAndSkip() {
+        // override this method and return the pattern for which endpoints to
+        // mock,
+        // and skip sending to the original endpoint.
+        return "direct:foo";
+    }
+
+    @Test
+    public void testMockEndpointAndSkip() throws Exception {
+        // notice we have automatic mocked the direct:foo endpoints and the name
+        // of the endpoints is "mock:uri"
+        getMockEndpoint("mock:result").expectedBodiesReceived("Hello World");
+        getMockEndpoint("mock:direct:foo").expectedMessageCount(1);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+
+        // the message was not send to the direct:foo route and thus not sent to
+        // the seda endpoint
+        SedaEndpoint seda = context.getEndpoint("seda:foo", SedaEndpoint.class);
+        assertEquals(0, seda.getCurrentQueueSize());
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start").to("direct:foo").to("mock:result");
+
+                from("direct:foo").transform(constant("Bye World")).to("seda:foo");
+            }
+        };
+    }
+}
+// end::e1[]
+// END SNIPPET: e1
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsFileTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsFileTest.java
new file mode 100644
index 0000000..1407f13
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsFileTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class IsMockEndpointsFileTest extends CamelTestSupport {
+
+    @Override
+    @BeforeEach
+    public void setUp() throws Exception {
+        deleteDirectory("target/input");
+        deleteDirectory("target/messages");
+        super.setUp();
+    }
+
+    @Override
+    public String isMockEndpoints() {
+        // override this method and return the pattern for which endpoints to
+        // mock.
+        return "file:target*";
+    }
+
+    @Test
+    public void testMockFileEndpoints() throws Exception {
+        // notice we have automatic mocked all endpoints and the name of the
+        // endpoints is "mock:uri"
+        MockEndpoint camel = getMockEndpoint("mock:file:target/messages/camel");
+        camel.expectedMessageCount(1);
+
+        MockEndpoint other = getMockEndpoint("mock:file:target/messages/others");
+        other.expectedMessageCount(1);
+
+        template.sendBodyAndHeader("file:target/input", "Hello Camel", Exchange.FILE_NAME, "camel.txt");
+        template.sendBodyAndHeader("file:target/input", "Hello World", Exchange.FILE_NAME, "world.txt");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("file:target/input").choice().when(bodyAs(String.class).contains("Camel")).to("file:target/messages/camel").otherwise().to("file:target/messages/others");
+            }
+        };
+    }
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsJUnit5Test.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsJUnit5Test.java
new file mode 100644
index 0000000..6b1fee9
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsJUnit5Test.java
@@ -0,0 +1,74 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+// START SNIPPET: e1
+// tag::e1[]
+public class IsMockEndpointsJUnit5Test extends CamelTestSupport {
+
+    @Override
+    public String isMockEndpoints() {
+        // override this method and return the pattern for which endpoints to
+        // mock.
+        // use * to indicate all
+        return "*";
+    }
+
+    @Test
+    public void testMockAllEndpoints() throws Exception {
+        // notice we have automatic mocked all endpoints and the name of the
+        // endpoints is "mock:uri"
+        getMockEndpoint("mock:direct:start").expectedBodiesReceived("Hello World");
+        getMockEndpoint("mock:direct:foo").expectedBodiesReceived("Hello World");
+        getMockEndpoint("mock:log:foo").expectedBodiesReceived("Bye World");
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye World");
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+
+        // additional test to ensure correct endpoints in registry
+        assertNotNull(context.hasEndpoint("direct:start"));
+        assertNotNull(context.hasEndpoint("direct:foo"));
+        assertNotNull(context.hasEndpoint("log:foo"));
+        assertNotNull(context.hasEndpoint("mock:result"));
+        // all the endpoints was mocked
+        assertNotNull(context.hasEndpoint("mock:direct:start"));
+        assertNotNull(context.hasEndpoint("mock:direct:foo"));
+        assertNotNull(context.hasEndpoint("mock:log:foo"));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start").to("direct:foo").to("log:foo").to("mock:result");
+
+                from("direct:foo").transform(constant("Bye World"));
+            }
+        };
+    }
+}
+// end::e1[]
+// END SNIPPET: e1
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsTest.java
new file mode 100644
index 0000000..afc9b78
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class IsMockEndpointsTest extends CamelTestSupport {
+
+    @Override
+    public String isMockEndpoints() {
+        return "*";
+    }
+
+    @Test
+    public void testMockAllEndpoints() throws Exception {
+        getMockEndpoint("mock:direct:start").expectedBodiesReceived("Hello World");
+        getMockEndpoint("mock:direct:foo").expectedBodiesReceived("Hello World");
+        getMockEndpoint("mock:log:foo").expectedBodiesReceived("Bye World");
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye World");
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+
+        // additional test to ensure correct endpoints in registry
+        assertNotNull(context.hasEndpoint("direct:start"));
+        assertNotNull(context.hasEndpoint("direct:foo"));
+        assertNotNull(context.hasEndpoint("log:foo"));
+        assertNotNull(context.hasEndpoint("mock:result"));
+        // all the endpoints was mocked
+        assertNotNull(context.hasEndpoint("mock:direct:start"));
+        assertNotNull(context.hasEndpoint("mock:direct:foo"));
+        assertNotNull(context.hasEndpoint("mock:log:foo"));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start").to("direct:foo").to("log:foo").to("mock:result");
+
+                from("direct:foo").transform(constant("Bye World"));
+            }
+        };
+    }
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MockEndpointFailNoHeaderTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MockEndpointFailNoHeaderTest.java
new file mode 100644
index 0000000..8746df2
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MockEndpointFailNoHeaderTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class MockEndpointFailNoHeaderTest extends CamelTestSupport {
+
+    @EndpointInject("mock:result")
+    protected MockEndpoint resultEndpoint;
+
+    @Produce("direct:start")
+    protected ProducerTemplate template;
+
+    @Override
+    public boolean isDumpRouteCoverage() {
+        return true;
+    }
+
+    @Test
+    public void withHeaderTestCase() throws InterruptedException {
+        String expectedBody = "<matched/>";
+        resultEndpoint.expectedHeaderReceived("foo", "bar");
+        template.sendBodyAndHeader(expectedBody, "foo", "bar");
+        resultEndpoint.assertIsSatisfied();
+    }
+
+    @Test
+    public void noHeaderTestCase() throws InterruptedException {
+        resultEndpoint.expectedHeaderReceived("foo", "bar");
+        resultEndpoint.setResultWaitTime(1); // speedup test
+        resultEndpoint.assertIsNotSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+            }
+        };
+    }
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MyProduceBean.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MyProduceBean.java
new file mode 100644
index 0000000..95d7263
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MyProduceBean.java
@@ -0,0 +1,32 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.Produce;
+
+/**
+ *
+ */
+public class MyProduceBean {
+
+    @Produce("mock:result")
+    MySender sender;
+
+    public void doSomething(String body) {
+        sender.send(body);
+    }
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MySender.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MySender.java
new file mode 100644
index 0000000..0188296
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MySender.java
@@ -0,0 +1,25 @@
+/*
+ * 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.junit5.patterns;
+
+/**
+ *
+ */
+public interface MySender {
+
+    void send(String body);
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/ProduceBeanTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/ProduceBeanTest.java
new file mode 100644
index 0000000..e72edb7
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/ProduceBeanTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ *
+ */
+public class ProduceBeanTest extends CamelTestSupport {
+
+    @Test
+    public void testProduceBean() throws Exception {
+        getMockEndpoint("mock:result").expectedMessageCount(1);
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start").bean(MyProduceBean.class, "doSomething");
+            }
+        };
+    }
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/RouteBuilderConfigureExceptionTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/RouteBuilderConfigureExceptionTest.java
new file mode 100644
index 0000000..46954b6
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/RouteBuilderConfigureExceptionTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.Predicate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class RouteBuilderConfigureExceptionTest extends CamelTestSupport {
+
+    private Predicate iAmNull;
+
+    @Override
+    @BeforeEach
+    public void setUp() throws Exception {
+        try {
+            super.setUp();
+            fail("Should have thrown exception");
+        } catch (Exception e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testFoo() throws Exception {
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("direct:start").choice().when(iAmNull).to("mock:dead");
+            }
+        };
+    }
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/RouteProcessorDumpRouteCoverageTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/RouteProcessorDumpRouteCoverageTest.java
new file mode 100644
index 0000000..fe820dd
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/RouteProcessorDumpRouteCoverageTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class RouteProcessorDumpRouteCoverageTest extends CamelTestSupport {
+
+    @Override
+    public boolean isDumpRouteCoverage() {
+        return true;
+    }
+
+    @Test
+    public void testProcessorJunit5() throws Exception {
+        String out = template.requestBody("direct:start", "Hello World", String.class);
+        assertEquals("Bye World", out);
+    }
+
+    @Override
+    @AfterEach
+    public void tearDown() throws Exception {
+        super.tearDown();
+        // should create that file when test is done
+        assertFileExists("target/camel-route-coverage/RouteProcessorDumpRouteCoverageTest-testProcessorJunit5.xml");
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start").process(exchange -> exchange.getMessage().setBody("Bye World"));
+            }
+        };
+    }
+
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleMockEndpointsTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleMockEndpointsTest.java
new file mode 100644
index 0000000..875bfad
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleMockEndpointsTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class SimpleMockEndpointsTest extends CamelTestSupport {
+
+    @Override
+    public String isMockEndpointsAndSkip() {
+        return "seda:queue";
+    }
+
+    @Test
+    public void testMockAndSkip() throws Exception {
+        getMockEndpoint("mock:seda:queue").expectedBodiesReceived("Bye Camel");
+
+        template.sendBody("seda:start", "Camel");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("seda:start").transform(simple("Bye ${body}")).to("seda:queue");
+            }
+        };
+    }
+
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleMockTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleMockTest.java
new file mode 100644
index 0000000..43b0429
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleMockTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class SimpleMockTest extends CamelTestSupport {
+
+    @Test
+    public void testMock() throws Exception {
+        getMockEndpoint("mock:result").expectedBodiesReceived("Hello World");
+
+        template.sendBody("direct:start", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:start").to("mock:result");
+            }
+        };
+    }
+
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleNotifyBuilderTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleNotifyBuilderTest.java
new file mode 100644
index 0000000..f44a03d
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleNotifyBuilderTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.junit5.patterns;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class SimpleNotifyBuilderTest extends CamelTestSupport {
+
+    @Test
+    public void testNotifyBuilder() throws Exception {
+        NotifyBuilder notify = new NotifyBuilder(context).from("seda:start").wereSentTo("seda:queue").whenDone(10).create();
+
+        for (int i = 0; i < 10; i++) {
+            template.sendBody("seda:start", "Camel" + i);
+        }
+
+        boolean matches = notify.matches(10, TimeUnit.SECONDS);
+        assertTrue(matches);
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("seda:start").transform(simple("Bye ${body}")).to("seda:queue");
+            }
+        };
+    }
+
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleWeaveAddMockLastTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleWeaveAddMockLastTest.java
new file mode 100644
index 0000000..b988275
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleWeaveAddMockLastTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.junit5.patterns;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.AdviceWithRouteBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.reifier.RouteReifier;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class SimpleWeaveAddMockLastTest extends CamelTestSupport {
+
+    public boolean isUseAdviceWith() {
+        return true;
+    }
+
+    @Test
+    public void testWeaveAddMockLast() throws Exception {
+        RouteReifier.adviceWith(context.getRouteDefinitions().get(0), context, new AdviceWithRouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                weaveAddLast().to("mock:result");
+            }
+        });
+        context.start();
+
+        getMockEndpoint("mock:result").expectedBodiesReceived("Bye Camel");
+
+        template.sendBody("seda:start", "Camel");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("seda:start").transform(simple("Bye ${body}")).to("seda:queue");
+            }
+        };
+    }
+
+}
diff --git a/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/UseOverridePropertiesWithPropertiesComponentTest.java b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/UseOverridePropertiesWithPropertiesComponentTest.java
new file mode 100644
index 0000000..ded346b
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/UseOverridePropertiesWithPropertiesComponentTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.junit5.patterns;
+
+import java.util.Properties;
+
+import org.apache.camel.builder.AdviceWithRouteBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.reifier.RouteReifier;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class UseOverridePropertiesWithPropertiesComponentTest extends CamelTestSupport {
+
+    @Override
+    public boolean isUseAdviceWith() {
+        return true;
+    }
+
+    @BeforeEach
+    public void doSomethingBefore() throws Exception {
+        AdviceWithRouteBuilder mocker = new AdviceWithRouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                replaceFromWith("direct:sftp");
+
+                interceptSendToEndpoint("file:*").skipSendToOriginalEndpoint().to("mock:file");
+            }
+        };
+        RouteReifier.adviceWith(context.getRouteDefinition("myRoute"), context, mocker);
+    }
+
+    @Override
+    protected Properties useOverridePropertiesWithPropertiesComponent() {
+        Properties pc = new Properties();
+        pc.put("ftp.username", "scott");
+        pc.put("ftp.password", "tiger");
+        return pc;
+    }
+
+    @Test
+    public void testOverride() throws Exception {
+        context.start();
+
+        getMockEndpoint("mock:file").expectedMessageCount(1);
+
+        template.sendBody("direct:sftp", "Hello World");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("ftp:somepath?username={{ftp.username}}&password={{ftp.password}}").routeId("myRoute").to("file:target/out");
+            }
+        };
+    }
+}
diff --git a/components/camel-test-junit5/src/test/resources/jndi.properties b/components/camel-test-junit5/src/test/resources/jndi.properties
new file mode 100644
index 0000000..7f9b32b
--- /dev/null
+++ b/components/camel-test-junit5/src/test/resources/jndi.properties
@@ -0,0 +1,22 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+# START SNIPPET: jndi
+
+java.naming.factory.initial = org.apache.camel.support.jndi.CamelInitialContextFactory
+
+# END SNIPPET: jndi
diff --git a/components/camel-test-junit5/src/test/resources/log4j2.properties b/components/camel-test-junit5/src/test/resources/log4j2.properties
new file mode 100644
index 0000000..53994e6
--- /dev/null
+++ b/components/camel-test-junit5/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-test.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.springframework.name = org.springframework
+logger.springframework.level = WARN
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
diff --git a/components/pom.xml b/components/pom.xml
index eb3529f..d50dfc3 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -66,6 +66,7 @@
         <module>camel-test-spring</module>
         <module>camel-testcontainers</module>
         <module>camel-testcontainers-spring</module>
+        <module>camel-test-junit5</module>
         <module>camel-blueprint</module>
         <module>camel-spring</module>
         <module>camel-groovy</module>