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/07 16:01:58 UTC

[camel] branch master updated: CAMEL-13826: Added a module that allows to write camel test with JUnit 5

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 6dde4af  CAMEL-13826: Added a module that allows to write camel test with JUnit 5
6dde4af is described below

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

    CAMEL-13826: Added a module that allows to write camel test with JUnit 5
---
 apache-camel/pom.xml                               |    5 +
 apache-camel/src/main/descriptors/common-bin.xml   |    1 +
 bom/camel-bom/pom.xml                              |    5 +
 components/camel-test-junit5/pom.xml               |   78 ++
 .../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    |   75 ++
 .../test/junit5/RouteFilterPatternExcludeTest.java |   56 +
 .../RouteFilterPatternIncludeExcludeTest.java      |   63 +
 .../test/junit5/RouteFilterPatternIncludeTest.java |   56 +
 .../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 +
 components/readme.adoc                             |    4 +-
 docs/components/modules/ROOT/nav.adoc              |    1 +
 .../components/modules/ROOT/pages/test-junit5.adoc |   46 +
 parent/pom.xml                                     |   22 +-
 .../pom.xml                                        |    1 +
 .../camel-spring-boot-dependencies/pom.xml         |    5 +
 52 files changed, 4496 insertions(+), 2 deletions(-)

diff --git a/apache-camel/pom.xml b/apache-camel/pom.xml
index 06efea4..dd3dc99 100644
--- a/apache-camel/pom.xml
+++ b/apache-camel/pom.xml
@@ -1474,6 +1474,11 @@
     </dependency>
     <dependency>
       <groupId>org.apache.camel</groupId>
+      <artifactId>camel-test-junit5</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
       <artifactId>camel-test-karaf</artifactId>
       <version>${project.version}</version>
     </dependency>
diff --git a/apache-camel/src/main/descriptors/common-bin.xml b/apache-camel/src/main/descriptors/common-bin.xml
index 35ad033..71c3226 100644
--- a/apache-camel/src/main/descriptors/common-bin.xml
+++ b/apache-camel/src/main/descriptors/common-bin.xml
@@ -315,6 +315,7 @@
         <include>org.apache.camel:camel-test</include>
         <include>org.apache.camel:camel-test-blueprint</include>
         <include>org.apache.camel:camel-test-cdi</include>
+        <include>org.apache.camel:camel-test-junit5</include>
         <include>org.apache.camel:camel-test-karaf</include>
         <include>org.apache.camel:camel-test-spring</include>
         <include>org.apache.camel:camel-testcontainers</include>
diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index 872384d..6dbf856 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -2875,6 +2875,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
+        <artifactId>camel-test-junit5</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
         <artifactId>camel-test-karaf</artifactId>
         <version>${project.version}</version>
       </dependency>
diff --git a/components/camel-test-junit5/pom.xml b/components/camel-test-junit5/pom.xml
new file mode 100644
index 0000000..9678e12
--- /dev/null
+++ b/components/camel-test-junit5/pom.xml
@@ -0,0 +1,78 @@
+<?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>
+		</dependency>
+		<dependency>
+			<groupId>org.junit.jupiter</groupId>
+			<artifactId>junit-jupiter-engine</artifactId>
+		</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..025c01e
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/CamelTestSupportTest.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;
+
+import org.apache.camel.NoSuchEndpointException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+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..a90649e
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternExcludeTest.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;
+
+import org.apache.camel.builder.RouteBuilder;
+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..a6ce59d
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeExcludeTest.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;
+
+import org.apache.camel.builder.RouteBuilder;
+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..7148557
--- /dev/null
+++ b/components/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeTest.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;
+
+import org.apache.camel.builder.RouteBuilder;
+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 ca6efcf..17b2cd0 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>
diff --git a/components/readme.adoc b/components/readme.adoc
index c4c46ab..e0a77ec 100644
--- a/components/readme.adoc
+++ b/components/readme.adoc
@@ -1051,7 +1051,7 @@ Number of Languages: 17 in 11 JAR artifacts (0 deprecated)
 == Miscellaneous Components
 
 // others: START
-Number of Miscellaneous Components: 33 in 33 JAR artifacts (0 deprecated)
+Number of Miscellaneous Components: 34 in 34 JAR artifacts (0 deprecated)
 
 [width="100%",cols="4,1,5",options="header"]
 |===
@@ -1111,6 +1111,8 @@ Number of Miscellaneous Components: 33 in 33 JAR artifacts (0 deprecated)
 
 | link:camel-test-cdi/src/main/docs/test-cdi.adoc[Test CDI] (camel-test-cdi) | 2.17 | Camel unit testing with CDI
 
+| link:camel-test-junit5/src/main/docs/test-junit5.adoc[Test Junit5] (camel-test-junit5) | 3.0 | Camel unit testing with JUnit 5
+
 | link:camel-test-karaf/src/main/docs/test-karaf.adoc[Test Karaf] (camel-test-karaf) | 2.18 | Camel integration testing with Apache Karaf
 
 | link:camel-test-spring/src/main/docs/test-spring.adoc[Test Spring] (camel-test-spring) | 2.10 | Camel unit testing with Spring
diff --git a/docs/components/modules/ROOT/nav.adoc b/docs/components/modules/ROOT/nav.adoc
index e05259a..c293f3d 100644
--- a/docs/components/modules/ROOT/nav.adoc
+++ b/docs/components/modules/ROOT/nav.adoc
@@ -343,6 +343,7 @@
 * xref:telegram-component.adoc[Telegram Component]
 * xref:test-blueprint.adoc[Blueprint Testing]
 * xref:test-cdi.adoc[CDI Testing]
+* xref:test-junit5.adoc[Test Module]
 * xref:test-karaf.adoc[Test Karaf]
 * xref:test-spring.adoc[Test Spring]
 * xref:test.adoc[Test Module]
diff --git a/docs/components/modules/ROOT/pages/test-junit5.adoc b/docs/components/modules/ROOT/pages/test-junit5.adoc
new file mode 100644
index 0000000..6624d90
--- /dev/null
+++ b/docs/components/modules/ROOT/pages/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/parent/pom.xml b/parent/pom.xml
index 359d80f..0928da1 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -388,7 +388,7 @@
         <junit-bundle-version>4.11_1</junit-bundle-version>
         <junit-toolbox-version>2.3</junit-toolbox-version>
         <junit-version>4.12</junit-version>
-        <junit-jupiter-version>5.3.1</junit-jupiter-version>
+        <junit-jupiter-version>5.5.1</junit-jupiter-version>
         <jxmpp-version>0.6.3</jxmpp-version>
         <jython-version>2.5.3</jython-version>
         <jzlib-version>1.1.3</jzlib-version>
@@ -2219,6 +2219,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
+        <artifactId>camel-test-junit5</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
         <artifactId>camel-test-karaf</artifactId>
         <version>${project.version}</version>
       </dependency>
@@ -4755,6 +4760,21 @@
                 <version>${junit-version}</version>
             </dependency>
             <dependency>
+                <groupId>org.junit.jupiter</groupId>
+                <artifactId>junit-jupiter-api</artifactId>
+                <version>${junit-jupiter-version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.junit.jupiter</groupId>
+                <artifactId>junit-jupiter-engine</artifactId>
+                <version>${junit-jupiter-version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.junit.jupiter</groupId>
+                <artifactId>junit-jupiter-vintage</artifactId>
+                <version>${junit-jupiter-version}</version>
+            </dependency>
+            <dependency>
                 <groupId>org.testng</groupId>
                 <artifactId>testng</artifactId>
                 <version>${testng-version}</version>
diff --git a/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies-generator/pom.xml b/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies-generator/pom.xml
index bd6957c..38b5af6 100644
--- a/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies-generator/pom.xml
+++ b/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies-generator/pom.xml
@@ -159,6 +159,7 @@
                             <exclude>com.google.code.gson:*</exclude>
                             <exclude>javax.servlet:javax.servlet-api</exclude>
                             <exclude>junit:*</exclude>
+                            <exclude>org.junit.jupiter:*</exclude>
                             <exclude>net.sf.ehcache:ehcache</exclude>
 
                             <exclude>xml-apis:*</exclude>
diff --git a/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml b/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml
index 1caf178..09b9eb0 100644
--- a/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml
+++ b/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml
@@ -3100,6 +3100,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
+        <artifactId>camel-test-junit5</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
         <artifactId>camel-test-karaf</artifactId>
         <version>${project.version}</version>
       </dependency>