You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2022/02/27 09:39:39 UTC

[camel] branch main updated: CAMEL-17690: camel-test-main - Annotation based testing (#7041)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 9fbfb3d  CAMEL-17690: camel-test-main - Annotation based testing (#7041)
9fbfb3d is described below

commit 9fbfb3dc5454b80689a12bc5e5910669c364b077
Author: Nicolas Filotto <es...@users.noreply.github.com>
AuthorDate: Sun Feb 27 10:39:00 2022 +0100

    CAMEL-17690: camel-test-main - Annotation based testing (#7041)
---
 .../camel-test/camel-test-main-junit5/pom.xml      |  15 +
 .../src/main/docs/test-main-junit5.adoc            | 405 +++++++++++++++++++-
 .../camel/test/main/junit5/AdviceRouteMapping.java |  63 ++++
 .../camel/test/main/junit5/CamelMainContext.java   | 420 +++++++++++++++++++++
 .../camel/test/main/junit5/CamelMainExtension.java | 178 +++++++++
 .../camel/test/main/junit5/CamelMainTest.java      | 198 ++++++++++
 .../test/main/junit5/CamelMainTestSupport.java     |  38 +-
 .../apache/camel/test/main/junit5/Configure.java   |  62 +++
 .../camel/test/main/junit5/DebuggerCallback.java   |  41 ++
 .../camel/test/main/junit5/MainForTest.java}       |  30 +-
 .../camel/test/main/junit5/ReplaceInRegistry.java  |  85 +++++
 .../junit5/{ => annotation}/AdviceRouteTest.java   |  40 +-
 .../DumpRouteCoverageTest.java}                    |  36 +-
 .../InheritanceTest.java}                          |  20 +-
 .../main/junit5/annotation/MockEndpointTest.java   |  93 +++++
 .../annotation/MockEndpointsAndSkipTest.java       |  83 ++++
 .../{ => annotation}/OverridePropertiesTest.java   |  32 +-
 .../ReplaceBeanFromFieldInheritanceTest.java}      |  16 +-
 .../ReplaceBeanFromFieldTest.java}                 |  44 ++-
 .../ReplaceBeanFromFieldWithMockTest.java          |  76 ++++
 .../ReplaceBeanFromMethodInheritanceTest.java}     |  16 +-
 .../ReplaceBeanFromMethodTest.java}                |  36 +-
 .../ReplaceBeanFromMethodWithMockTest.java         |  70 ++++
 .../{ => annotation}/ReplaceRouteFromTest.java     |  31 +-
 .../annotation/SupportParameterizedTest.java       |  55 +++
 .../junit5/annotation/SupportRepeatedTest.java     |  55 +++
 .../junit5/annotation/TestInstanceDefaultTest.java |  61 +++
 .../annotation/TestInstancePerClassTest.java       |  69 ++++
 .../annotation/TestInstancePerMethodTest.java      |  63 ++++
 ...UseSeveralPropertyPlaceholderLocationsTest.java |  26 +-
 .../WithConfigurationClassTest.java}               |  28 +-
 .../annotation/WithDebuggerCallbackTest.java       |  71 ++++
 .../junit5/{ => annotation}/WithMainClassTest.java |  24 +-
 .../LoadCustomConfigurationDefaultPackageTest.java |  26 +-
 .../LoadCustomConfigurationSamePackageTest.java    |  28 +-
 .../EchoRouteBuilder.java}                         |  15 +-
 .../test/main/junit5/{ => common}/Greetings.java   |   2 +-
 .../main/junit5/{ => common}/MyConfiguration.java  |   2 +-
 .../{Greetings.java => common/MyMainClass.java}    |  14 +-
 .../MySatefulRouteBuilder.java}                    |  15 +-
 .../{Greetings.java => common/MyStatefulBean.java} |  15 +-
 .../main/junit5/{ => legacy}/AdviceRouteTest.java  |   4 +-
 .../{ => legacy}/OverridePropertiesTest.java       |   4 +-
 .../main/junit5/{ => legacy}/ReplaceBeanTest.java  |   5 +-
 .../ReplaceBeanWithMockTest.java}                  |  40 +-
 .../junit5/{ => legacy}/ReplaceRouteFromTest.java  |   4 +-
 .../SupportParameterizedTest.java}                 |  27 +-
 .../SupportRepeatedTest.java}                      |  27 +-
 .../junit5/legacy/TestInstanceDefaultTest.java     |  59 +++
 .../junit5/legacy/TestInstancePerClassTest.java    |  61 +++
 .../junit5/legacy/TestInstancePerMethodTest.java   |  61 +++
 ...UseSeveralPropertyPlaceholderLocationsTest.java |   4 +-
 .../junit5/{ => legacy}/WithMainClassTest.java     |   7 +-
 .../LoadCustomConfigurationDefaultPackageTest.java |   4 +-
 .../LoadCustomConfigurationSamePackageTest.java    |   4 +-
 .../custom-same-package-application.properties     |   0
 .../custom-same-package-application.properties     |   0
 .../src/test/resources/routes/my-route.xml         |   2 +-
 58 files changed, 2666 insertions(+), 344 deletions(-)

diff --git a/components/camel-test/camel-test-main-junit5/pom.xml b/components/camel-test/camel-test-main-junit5/pom.xml
index 5be74c4..6843a45 100644
--- a/components/camel-test/camel-test-main-junit5/pom.xml
+++ b/components/camel-test/camel-test-main-junit5/pom.xml
@@ -53,6 +53,21 @@
             <artifactId>camel-xml-io-dsl</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-management</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-xml-jaxb</artifactId>
+            <scope>test</scope>
+        </dependency>
         <!-- logging -->
         <dependency>
             <groupId>org.apache.logging.log4j</groupId>
diff --git a/components/camel-test/camel-test-main-junit5/src/main/docs/test-main-junit5.adoc b/components/camel-test/camel-test-main-junit5/src/main/docs/test-main-junit5.adoc
index 073bf2a..eca51be 100644
--- a/components/camel-test/camel-test-main-junit5/src/main/docs/test-main-junit5.adoc
+++ b/components/camel-test/camel-test-main-junit5/src/main/docs/test-main-junit5.adoc
@@ -10,7 +10,13 @@
 
 The `camel-test-main-junit5` module is used for unit testing Camel launched in Standalone mode with Camel Main.
 
-The class `org.apache.camel.test.main.junit5.CamelMainTestSupport` is the base class of all the unit test classes of a Camel Main application. It provides the ability to configure and launch Camel like a Camel Main application.
+This module proposes 2 approaches to configure and launch Camel like a Camel Main application for testing purpose.
+
+The *legacy* approach consisting of extending the base class `org.apache.camel.test.main.junit5.CamelMainTestSupport` and overriding the appropriate methods to enable or disable a feature.
+
+The *annotation* based approach consisting of annotating the test classes with `org.apache.camel.test.main.junit5.CamelMainTest` with the appropriate attributes to enable or disable a feature.
+
+In the next sections, for each use case both approaches are proposed with the labels *legacy* and *annotation* to differentiate them.
 
 Maven users will need to add the following dependency to
 their `pom.xml` for this component:
@@ -28,10 +34,19 @@ their `pom.xml` for this component:
 
 == Specify a main class
 
-Most of the time, a Camel Main application has a main class from which all the Camel related classes are found. In practice, this is done simply by providing the main class of the application in the constructor of Camel Main like for example `new Main(SomeApplication.class)` where `SomeApplication.class` is the main class of the application. The same behavior can be simulated with `CamelMainTestSupport` by overriding the method `getMainClass()` to provide the main class of the applicatio [...]
+Most of the time, a Camel Main application has a main class from which all the Camel related classes are found.
+
+In practice, this is done simply by providing the main class of the application in the constructor of Camel Main like for example `new Main(SomeApplication.class)` where `SomeApplication.class` is the main class of the application.
 
-In the next example, the test class `SomeTest` indicates that the class `SomeMainClass` is the main class of the application to test.
+=== Legacy ===
+The same behavior can be simulated with `CamelMainTestSupport` by overriding the method `getMainClass()` to provide the main class of the application to test.
 
+=== Annotation ===
+The same behavior can be simulated with `CamelMainTest` by setting the attribute `mainClass` to provide the main class of the application to test.
+
+In the next examples, the main class of the application to test is the class `SomeMainClass`.
+
+=== Legacy ===
 [source,java]
 ----
 class SomeTest extends CamelMainTestSupport {
@@ -45,16 +60,32 @@ class SomeTest extends CamelMainTestSupport {
 }
 ----
 
+=== Annotation ===
+[source,java]
+----
+@CamelMainTest(mainClass = SomeMainClass.class)
+class SomeTest {
+
+    // Rest of the test class
+}
+----
+
 == Configure Camel as a Camel Main application
 
-A Camel Main application has access to many specific configuration properties that are not available from the base class `CamelTestSupport`. The base class `CamelMainTestSupport` provides the method `configure(MainConfigurationProperties configuration)` that can be overridden in order to configure Camel for the test like a Camel Main application.
+A Camel Main application has access to many specific configuration properties that are not available from the base class `CamelTestSupport`.
+
+=== Legacy ===
+The base class `CamelMainTestSupport` provides the method `configure(MainConfigurationProperties configuration)` that can be overridden in order to configure Camel for the test like a Camel Main application.
 
-In the next example, the test class `SomeTest` adds a configuration class and specifies the xml routes to include.
+=== Annotation ===
+The annotation `Configure` allows to mark a method with an arbitrary name and a parameter of type `MainConfigurationProperties` to be called in order to configure Camel for the test like a Camel Main application. Several methods in the test class and/or its parent classes can be annotated.
 
+In the next examples, the test class `SomeTest` adds a configuration class and specifies the xml routes to include.
+
+=== Legacy ===
 [source,java]
 ----
 import org.apache.camel.main.MainConfigurationProperties;
-import org.apache.camel.test.main.junit5.CamelMainTestSupport;
 
 class SomeTest extends CamelMainTestSupport {
 
@@ -70,16 +101,44 @@ class SomeTest extends CamelMainTestSupport {
 }
 ----
 
+=== Annotation ===
+[source,java]
+----
+import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.test.main.junit5.Configure;
+
+@CamelMainTest
+class SomeTest {
+
+    @Configure
+    protected void configure(MainConfigurationProperties configuration) {
+        // Add a configuration class
+        configuration.addConfiguration(SomeConfiguration.class);
+        // Add all the XML routes
+        configuration.withRoutesIncludePattern("routes/*.xml");
+    }
+
+    // Rest of the test class
+}
+----
+
 == Configure a custom property placeholder location
 
-By default, the property placeholder used is `application.properties` from the default package. There are several ways to configure the location of the property placeholders, you can either provide the file name of the property placeholder or a list of locations.
+By default, the property placeholder used is `application.properties` from the default package. There are several ways to configure the property placeholder locations, you can either provide the file name of the property placeholder or a list of locations.
 
 === A list of property placeholder locations
 
-To provide a comma separated list of location, you can override the method `getPropertyPlaceholderLocations()` to return your custom list of locations. The order in the list matter especially in case of a property defined at several locations, the value of the property found in the first location where it is defined, is used.
+=== Legacy ===
+The method `getPropertyPlaceholderLocations()` can be overridden to provide a comma separated list of locations.
+
+=== Annotation ===
+The attribute `propertyPlaceholderLocations` can be set to provide a list of locations.
 
-In the next example, the property placeholder locations configured are `extra-application.properties` and `application.properties` both available in the default package.
+The order in the list matter especially in case of a property defined at several locations, the value of the property found in the first location where it is defined, is used.
 
+In the next examples, the property placeholder locations configured are `extra-application.properties` and `application.properties` both available in the default package.
+
+=== Legacy ===
 [source,java]
 ----
 class SomeTest extends CamelMainTestSupport {
@@ -93,14 +152,33 @@ class SomeTest extends CamelMainTestSupport {
 }
 ----
 
+=== Annotation ===
+[source,java]
+----
+@CamelMainTest(propertyPlaceholderLocations = { "classpath:extra-application.properties", "classpath:application.properties" })
+class SomeTest {
+
+    // Rest of the test class
+}
+----
+
 === The file name of the property placeholder
 
-For the sake of simplicity, in case you need only one property placeholder location, you can specify the file name of the property placeholder by overriding the method `getPropertyPlaceholderFileName()`. It can then infer the locations of the property placeholder as it assumes that it is located either in the same package as the test class or directly in the default package.
+For the sake of simplicity, in case you need only one property placeholder location.
+
+=== Legacy ===
+The method `getPropertyPlaceholderFileName()` can be overridden to provide the file name of the property placeholder.
+
+=== Annotation ===
+The attribute `propertyPlaceholderFileName` can be set to provide the file name of the property placeholder.
 
-In the next example, the method `getPropertyPlaceholderFileName()` has been overridden in the test class `com.somecompany.SomeTest` to return `custom-application.properties` indicating that the actual possible locations of the property placeholder are `classpath:com/somecompany/custom-application.properties;optional=true,classpath:custom-application.properties;optional=true` which means that for each property to find, it tries to get it first from the properties file of the same package  [...]
+It can then infer the locations of the property placeholder as it assumes that it is located either in the same package as the test class or directly in the default package.
+
+In the next examples, since the test class is `com.somecompany.SomeTest` and the file name of the property placeholder is `custom-application.properties` , the actual possible locations of the property placeholder are `classpath:com/somecompany/custom-application.properties;optional=true,classpath:custom-application.properties;optional=true` which means that for each property to find, it tries to get it first from the properties file of the same package if it exists and if it cannot be f [...]
 
 NOTE: Since the properties files are declared as optional, no exception is raised if they are both absent.
 
+=== Legacy ===
 [source,java]
 ----
 package com.somecompany;
@@ -116,14 +194,34 @@ class SomeTest extends CamelMainTestSupport {
 }
 ----
 
+=== Annotation ===
+[source,java]
+----
+package com.somecompany;
+
+@CamelMainTest(propertyPlaceholderFileName = "custom-application.properties")
+class SomeTest {
+
+    // Rest of the test class
+}
+----
+
 == Replace an existing bean
 
 In Camel Main, you have the opportunity to bind custom beans dynamically using the specific annotation `@BindToRegistry` which is very helpful but for testing purpose, you may need to replace the bean by a mock or a test implementation.
 
+=== Legacy ===
 To bind additional beans, you can still override the well known method `bindToRegistry(Registry registry)` but this method cannot be used to replace a bean created and bound automatically by Camel as it is called too early in the initialization process of Camel. To work around this problem, you can instead bind your beans by overriding the new method `bindToRegistryAfterInjections(Registry registry)` which is called after existing injections and automatic binding have been done.
 
-In the next example, an instance of a custom bean of type `CustomGreetings` could be created from the value of property `name` and is used to replace the bean of type `Greetings` automatically bound by Camel with the name `myGreetings`.
+=== Annotation ===
+The annotation `ReplaceInRegistry` allows to mark a method or a field to replace an existing bean in the registry.
 
+* In case of a field, the name and its type are used to identify the bean to replace, and the value of the field is the new value of the bean. The field can be in the test class or in a parent class.
+* In case of a method, the name and its return type are used to identify the bean to replace, and the return value of the method is the new value of the bean. The method can be in the test class or in a parent class.
+
+In the next examples, an instance of a custom bean of type `CustomGreetings` is used to replace the bean of type `Greetings` automatically bound by Camel with the name `myGreetings`.
+
+=== Legacy ===
 [source,java]
 ----
 class SomeTest extends CamelMainTestSupport {
@@ -138,4 +236,285 @@ class SomeTest extends CamelMainTestSupport {
 
     // Rest of the test class
 }
-----
\ No newline at end of file
+----
+
+=== Annotation ===
+
+==== Using a field ====
+
+[source,java]
+----
+import org.apache.camel.test.main.junit5.ReplaceInRegistry;
+
+@CamelMainTest
+class SomeTest {
+
+    @ReplaceInRegistry
+    Greetings myGreetings = new CustomGreetings("Willy"); // <1>
+
+    // Rest of the test class
+}
+----
+<1> We cannot rely on the value of property that is injected thanks to `@PropertyInject` like in the previous code snippet because the injection occurs after the instantiation of the test class, so it would be `null`.
+
+==== Using a method ====
+
+[source,java]
+----
+import org.apache.camel.test.main.junit5.ReplaceInRegistry;
+
+@CamelMainTest
+class SomeTest {
+
+    @PropertyInject("name")
+    String name;
+
+    @ReplaceInRegistry
+    Greetings myGreetings() {
+        return new CustomGreetings(name);
+    }
+
+    // Rest of the test class
+}
+----
+
+== Override existing properties
+
+Some properties are inherited from properties file like the `application.properties` and need to be overridden within the context of the test.
+
+=== Legacy ===
+The method `useOverridePropertiesWithPropertiesComponent()` can be overridden to provide an instance of type `java.util.Properties` that contains the properties to override.
+
+=== Annotation ===
+The attribute `properties` can be set to provide an array of `String` representing the key/value pairs of properties to override in the following order `property-key-1, property-value-1, property-key-2, ...`.
+
+In the next examples, the value of the property whose name is `host` is replaced with `localhost`.
+
+=== Legacy ===
+[source,java]
+----
+import static org.apache.camel.util.PropertiesHelper.asProperties;
+
+class SomeTest extends CamelMainTestSupport {
+
+    @Override
+    protected Properties useOverridePropertiesWithPropertiesComponent() {
+        return asProperties("host", "localhost");
+    }
+
+    // Rest of the test class
+}
+----
+
+=== Annotation ===
+[source,java]
+----
+@CamelMainTest(properties = { "host", "localhost" })
+class SomeTest {
+
+    // Rest of the test class
+}
+----
+
+== Replace from endpoints
+
+To be able to test easily the behavior of a route without being affected by the type of from endpoint used in the route, it can be very helpful to replace the from endpoint with an endpoint more test friendly.
+
+=== Legacy ===
+The method `replaceRouteFromWith()` can be called to provide the id of the route to modify and the URI of the new from endpoint.
+
+=== Annotation ===
+The attribute `replaceRouteFromWith` can be set to provide an array of `String` representing a list of id of the route to modify and the URI of the new from endpoint in the following order `route-id-1, new-uri-1, route-id-2, ...`.
+
+In the next examples, the route whose id is `main-route` is advised to replace its current from endpoint with a `direct:main` endpoint.
+
+=== Legacy ===
+[source,java]
+----
+class SomeTest extends CamelMainTestSupport {
+
+    @Override
+    @BeforeEach
+    public void setUp() throws Exception {
+        replaceRouteFromWith("main-route", "direct:main");
+        super.setUp();
+    }
+
+    // Rest of the test class
+}
+----
+
+=== Annotation ===
+[source,java]
+----
+@CamelMainTest(replaceRouteFromWith = { "main-route", "direct:main" })
+class SomeTest {
+
+    // Rest of the test class
+}
+----
+
+== Configure additional camel configuration classes
+
+In practice, additional camel configuration classes can be provided for the sake of simplicity directly from the constructor of the Camel Main like for example `new Main(SomeApplication.class, SomeCamelConfiguration.class)` where `SomeApplication.class` is the main class of the application and `SomeCamelConfiguration.class` is an additional camel configuration class.
+
+=== Legacy ===
+There is no specific method for that, but it can be done by overriding the method `configure(MainConfigurationProperties configuration)` like described in a previous section.
+
+=== Annotation ===
+The attribute `configurationClasses` can be set to provide an array of additional camel configuration classes.
+
+In the next examples, the camel configuration class `SomeCamelConfiguration` is added to the global configuration.
+
+=== Legacy ===
+[source,java]
+----
+class SomeTest extends CamelMainTestSupport {
+
+    @Override
+    protected void configure(MainConfigurationProperties configuration) {
+        // Add the configuration class
+        configuration.addConfiguration(SomeCamelConfiguration.class);
+    }
+
+    // Rest of the test class
+}
+----
+
+=== Annotation ===
+[source,java]
+----
+@CamelMainTest(configurationClasses = SomeCamelConfiguration.class)
+class SomeTest {
+
+    // Rest of the test class
+}
+----
+
+== Advice a route
+
+It is possible to modify a route within the context of a test by using advices generally represented by specific route builders of type `AdviceWithRouteBuilder` as it proposes out-of-box utility methods allowing to advice a route easily.
+
+=== Legacy ===
+A route needs to be advised directly in the test method using one of the utility method `AdviceWith.adviceWith` and the Camel context has to be started explicitly once the route has been advised to take it into account.
+
+=== Annotation ===
+The attribute `advices` can be set to provide an array of annotations of type `AdviceRouteMapping` representing a mapping between a route to advice and the corresponding route builders to call to advice the route. As the route builders are instantiated using the default constructor, make sure that the default constructor exists.
+
+In the next examples, the route whose id is `main-route` is advised to replace its current from endpoint with a `direct:main` endpoint.
+
+=== Legacy ===
+[source,java]
+----
+class SomeTest extends CamelMainTestSupport {
+
+    @Override
+    public boolean isUseAdviceWith() { // <1>
+        return true;
+    }
+
+    @Test
+    void someTest() throws Exception {
+        // Advice the route by replace the from endpoint
+        AdviceWith.adviceWith(context, "main-route", ad -> ad.replaceFromWith("direct:main")); // <2>
+
+        // must start Camel after we are done using advice-with
+        context.start(); // <3>
+
+        // Rest of the test method
+    }
+
+    // Rest of the test class
+}
+----
+<1> Override the method `isUseAdviceWith` to return `true` indicating that the Camel context should not be started before calling the test method as there is at least one route to advise.
+<2> Call a utility method `AdviceWith.adviceWith` to advice a route
+<3> Start the Camel context as it was not yet started
+
+=== Annotation ===
+[source,java]
+----
+@CamelMainTest(advices = @AdviceRouteMapping(route = "main-route", advice = SomeTest.SomeRouteBuilder.class))
+class SomeTest {
+
+    static class SomeRouteBuilder extends AdviceWithRouteBuilder {
+
+        @Override
+        public void configure() throws Exception {
+            replaceFromWith("direct:main");
+        }
+    }
+
+    // Rest of the test class
+}
+----
+
+== Mock and skip an endpoint
+
+For testing purpose, it can be helpful to mock only or to mock and skip all the endpoints matching with a given pattern.
+
+=== Legacy ===
+The method `isMockEndpoints()` can be overridden to provide the pattern that should match with the endpoints to mock.
+The method `isMockEndpointsAndSkip()` can be overridden to provide the pattern that should match with the endpoints to mock and skip.
+
+=== Annotation ===
+The attribute `mockEndpoints` can be set to provide the pattern that should match with the endpoints to mock.
+The attribute `mockEndpointsAndSkip` can be set to provide the pattern that should match with the endpoints to mock and skip.
+
+In the next examples, the endpoints whose URI starts with `direct:` are mocked.
+
+=== Legacy ===
+[source,java]
+----
+class SomeTest extends CamelMainTestSupport {
+
+    @Override
+    public String isMockEndpoints() {
+        return "direct:*";
+    }
+
+    // Rest of the test class
+}
+----
+
+=== Annotation ===
+[source,java]
+----
+@CamelMainTest(mockEndpoints = "direct:*")
+class SomeTest {
+
+    // Rest of the test class
+}
+----
+
+== Dump route coverage
+
+It is possible to dump the route coverage of a given test. This feature needs JMX to be enabled which is done automatically when the feature itself is enabled, it also means that the `camel-management` has to be part of the dependencies of the project to be able to use it. The feature can be enabled globally by setting the system property `CamelTestRouteCoverage` to `true`.
+
+The result is generated in `target/camel-route-coverage/${class-name}-${test-name}.xml`.
+
+=== Legacy ===
+The method `isDumpRouteCoverage()` can be overridden to return `true` indicating that the feature is enabled.
+
+=== Annotation ===
+The attribute `dumpRouteCoverage` can be set to `true` indicating that the feature is enabled.
+
+== Override the shutdown timeout
+
+The default shutdown timeout of Camel is not really adapted for a test as it can be very long. This feature allows to override it to 10 seconds by default, but it can also be set to a custom value knowing that it is expressed in seconds.
+
+=== Legacy ===
+The method `getShutdownTimeout()` can be overridden to return the expected shutdown timeout.
+
+=== Annotation ===
+The attribute `shutdownTimeout` can be set to the expected shutdown timeout.
+
+== Debug mode
+
+For debugging purpose, it is possible to be called before and after invoking a processor allowing to log specific messages or add breakpoints in your favorite IDE.
+
+=== Legacy ===
+The method `isUseDebugger()` can be overridden to return `true` indicating that the feature is enabled. The methods `debugBefore` and `debugAfter` can then be overridden to execute some specific code for debugging purpose.
+
+=== Annotation ===
+The test class needs to implement the interface `org.apache.camel.test.main.junit5.DebuggerCallback` to enable the feature. The methods `debugBefore` and `debugAfter` can then be implemented to execute some specific code for debugging purpose.
diff --git a/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/AdviceRouteMapping.java b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/AdviceRouteMapping.java
new file mode 100644
index 0000000..2fe29d6
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/AdviceRouteMapping.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.main.junit5;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import org.apache.camel.builder.RouteBuilder;
+
+/**
+ * A specific annotation allowing to define a mapping between a route represented by its id and advice represented by a
+ * subclass of {@link RouteBuilder} to use to advice the route. To advice a route, an instance of {@link RouteBuilder}
+ * needs to be created and for this the default constructor is used, so make sure that a default constructor exists.
+ * <p/>
+ * This annotation is only meant to be used inside the annotation {@link CamelMainTest} to define all the advices to
+ * consider.
+ * <p/>
+ * In the next example, the annotation {@code CamelMainTest} on the test class {@code SomeTest} indicates that the
+ * {@code RouteBuilder} of type {@code ReplaceDirectWithMockBuilder} should be used to advise the route whose identifier
+ * is <i>main-route</i>.
+ *
+ * <pre>
+ * <code>
+ *
+ * &#64;CamelMainTest(advices = @AdviceRouteMapping(route = "main-route", advice = ReplaceDirectWithMockBuilder.class))
+ * class SomeTest {
+ *     // The rest of the test class
+ * }
+ * </code>
+ * </pre>
+ *
+ * @see RouteBuilder
+ * @see CamelMainTest
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AdviceRouteMapping {
+
+    /**
+     * @return the id of the route to <i>advice</i>.
+     */
+    String route();
+
+    /**
+     * @return the class of the specific route builder that is used to advice the route.
+     */
+    Class<? extends RouteBuilder> advice();
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/CamelMainContext.java b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/CamelMainContext.java
new file mode 100644
index 0000000..fd9de35
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/CamelMainContext.java
@@ -0,0 +1,420 @@
+/*
+ * 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.main.junit5;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.camel.CamelConfiguration;
+import org.apache.camel.Exchange;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.NamedNode;
+import org.apache.camel.Processor;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.builder.AdviceWithRouteBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.InterceptSendToMockEndpointStrategy;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.impl.debugger.DefaultDebugger;
+import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.spi.CamelBeanPostProcessor;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.support.BreakpointSupport;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.platform.commons.support.HierarchyTraversalMode;
+
+import static org.apache.camel.support.ObjectHelper.invokeMethod;
+import static org.apache.camel.util.ReflectionHelper.doWithFields;
+import static org.apache.camel.util.ReflectionHelper.getField;
+import static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedMethods;
+
+/**
+ * An internal class representing the context of the test that is stored by the extension and closed automatically by
+ * JUnit 5.
+ */
+final class CamelMainContext implements ExtensionContext.Store.CloseableResource {
+
+    /**
+     * The Camel context used for the test.
+     */
+    private final ModelCamelContext camelContext;
+
+    /**
+     * Construct a {@code CamelMainContext} with the given Camel context.
+     * 
+     * @param camelContext the Camel context used for the test.
+     */
+    private CamelMainContext(ModelCamelContext camelContext) {
+        this.camelContext = camelContext;
+    }
+
+    /**
+     * @return the Camel context used for the test.
+     */
+    ModelCamelContext context() {
+        return camelContext;
+    }
+
+    /**
+     * Start the underlying Camel context.
+     */
+    void start() {
+        camelContext.start();
+    }
+
+    @Override
+    public void close() throws Exception {
+        camelContext.close();
+    }
+
+    /**
+     * @param  context the extension context from which all the data needed to create an instance of
+     *                 {@code CamelMainContext} is extracted
+     * @return         a new instance of a {@code Builder}
+     */
+    static Builder builder(ExtensionContext context) {
+        return new Builder(context);
+    }
+
+    /**
+     * An inner class used to build an instance of {@code CamelMainContext}.
+     */
+    static final class Builder {
+
+        /**
+         * The type of the test class to execute.
+         */
+        private final Class<?> requiredTestClass;
+        /**
+         * The annotation {@code CamelMainTest} extracted from the test class to execute.
+         */
+        private final CamelMainTest annotation;
+        /**
+         * The instance of the test class to execute.
+         */
+        private final Object instance;
+        /**
+         * The flag indicating whether JMX should be enabled.
+         */
+        private boolean useJmx;
+
+        /**
+         * Construct a {@code Builder} with the given extension context.
+         * 
+         * @param context the extension context from which all the data needed to create an instance of
+         *                {@code CamelMainContext} is extracted
+         */
+        private Builder(ExtensionContext context) {
+            this.requiredTestClass = context.getRequiredTestClass();
+            this.annotation = requiredTestClass.getAnnotation(CamelMainTest.class);
+            this.instance = context.getRequiredTestInstance();
+        }
+
+        Builder useJmx(boolean useJmx) {
+            this.useJmx = useJmx;
+            return this;
+        }
+
+        /**
+         * Build the {@code CamelMainContext} and its underlying Camel context based on the data extracted from the
+         * annotation {@code CamelMainTest}.
+         *
+         * @return           a new instance of {@code CamelMainContext} with its underlying Camel context initialized.
+         * @throws Exception if an error occurs while initializing the Camel context.
+         */
+        CamelMainContext build() throws Exception {
+            final ModelCamelContext camelContext = new DefaultCamelContext();
+            final ExtendedCamelContext extendedCamelContext = camelContext.getExtension(ExtendedCamelContext.class);
+            mockEndpointsIfNeeded(extendedCamelContext);
+            configureShutdownTimeout(camelContext);
+            configureDebuggerIfNeeded(camelContext);
+            initCamelContext(camelContext);
+            final CamelBeanPostProcessor beanPostProcessor = extendedCamelContext.getBeanPostProcessor();
+            initInstance(beanPostProcessor);
+            replaceBeansInRegistry(camelContext.getRegistry());
+            applyReplaceRouteFromWith(camelContext);
+            adviceRoutes(camelContext, beanPostProcessor);
+            return new CamelMainContext(camelContext);
+        }
+
+        /**
+         * Configure the shutdown timeout to avoid waiting for too long.
+         */
+        private void configureShutdownTimeout(ModelCamelContext context) {
+            context.getShutdownStrategy().setTimeout(annotation.shutdownTimeout());
+        }
+
+        /**
+         * Inject all the Camel related object instances into the test instance.
+         */
+        private void initInstance(CamelBeanPostProcessor beanPostProcessor) throws Exception {
+            beanPostProcessor.postProcessBeforeInitialization(instance, requiredTestClass.getName());
+            beanPostProcessor.postProcessAfterInitialization(instance, requiredTestClass.getName());
+        }
+
+        /**
+         * Mock the endpoints corresponding to the patterns provided by {@link CamelMainTest#mockEndpoints()} and
+         * {@link CamelMainTest#mockEndpointsAndSkip()} if any.
+         */
+        private void mockEndpointsIfNeeded(ExtendedCamelContext context) {
+            // enable auto mocking if enabled
+            final String mockEndpoints = annotation.mockEndpoints();
+            if (!mockEndpoints.isEmpty()) {
+                context.registerEndpointCallback(new InterceptSendToMockEndpointStrategy(mockEndpoints));
+            }
+            final String mockEndpointsAndSkip = annotation.mockEndpointsAndSkip();
+            if (!mockEndpointsAndSkip.isEmpty()) {
+                context.registerEndpointCallback(new InterceptSendToMockEndpointStrategy(mockEndpointsAndSkip, true));
+            }
+        }
+
+        /**
+         * Configure the debug mode if the test instance is of type {@link DebuggerCallback} in a such way that the
+         * callback methods {@link DebuggerCallback#debugBefore} and {@link DebuggerCallback#debugAfter} are called when
+         * executing the routes.
+         */
+        private void configureDebuggerIfNeeded(ModelCamelContext context) {
+            if (instance instanceof DebuggerCallback) {
+                context.setDebugging(true);
+                context.setDebugger(new DefaultDebugger());
+                DebuggerCallback callback = (DebuggerCallback) instance;
+                context.getDebugger().addBreakpoint(new BreakpointSupport() {
+                    @Override
+                    public void beforeProcess(Exchange exchange, Processor processor, NamedNode definition) {
+                        callback.debugBefore(exchange, processor, (ProcessorDefinition<?>) definition, definition.getId(),
+                                definition.getLabel());
+                    }
+
+                    @Override
+                    public void afterProcess(Exchange exchange, Processor processor, NamedNode definition, long timeTaken) {
+                        callback.debugAfter(exchange, processor, (ProcessorDefinition<?>) definition, definition.getId(),
+                                definition.getLabel(), timeTaken);
+                    }
+                });
+            }
+        }
+
+        /**
+         * Initialize the given Camel context like a Camel Main application based on what could be extracted from the
+         * annotation {@link CamelMainTest}.
+         */
+        private void initCamelContext(ModelCamelContext context) throws Exception {
+            createMainForTest().init(context);
+        }
+
+        /**
+         * Create a new Camel Main application for test based on what could be extracted from the annotation
+         * {@link CamelMainTest}.
+         */
+        private MainForTest createMainForTest() {
+            final MainForTest main = new MainForTest();
+            configureMainClass(main);
+            addConfigurationClasses(main);
+            main.configure().setJmxEnabled(useJmx);
+            configureOverrideProperties(main);
+            configurePropertyPlaceholderLocations(main);
+            invokeConfigureMethods(main);
+            return main;
+        }
+
+        /**
+         * Configure the main class to use in the given Camel Main application for test.
+         */
+        private void configureMainClass(MainForTest main) {
+            final Class<?> mainClass = annotation.mainClass();
+            if (mainClass != void.class) {
+                main.configure().withBasePackageScan(mainClass.getPackageName());
+            }
+        }
+
+        /**
+         * Add the additional configuration classes to the global configuration.
+         */
+        private void addConfigurationClasses(MainForTest main) {
+            // Add the configuration classes if any
+            for (Class<? extends CamelConfiguration> configurationClass : annotation.configurationClasses()) {
+                main.configure().addConfiguration(configurationClass);
+            }
+        }
+
+        /**
+         * Instantiate automatically all the route builders retrieved from the annotations {@link AdviceRouteMapping}
+         * using the default constructor, then inject all the Camel related object instances into the created instances
+         * and finally use these created instances to advice the routes corresponding to
+         * {@link AdviceRouteMapping#route()}.
+         *
+         * @throws Exception if a route builder could not be created or initialized, or if a route could not be advised.
+         */
+        private void adviceRoutes(ModelCamelContext context, CamelBeanPostProcessor beanPostProcessor) throws Exception {
+            for (AdviceRouteMapping adviceRouteMapping : annotation.advices()) {
+                final Class<? extends RouteBuilder> adviceClass = adviceRouteMapping.advice();
+                try {
+                    final Constructor<?> constructor = adviceClass.getDeclaredConstructor();
+                    if (constructor.trySetAccessible()) {
+                        RouteBuilder advice = adviceClass.cast(constructor.newInstance());
+                        beanPostProcessor.postProcessBeforeInitialization(advice, advice.getClass().getName());
+                        beanPostProcessor.postProcessAfterInitialization(advice, advice.getClass().getName());
+                        AdviceWith.adviceWith(context.getRouteDefinition(adviceRouteMapping.route()), context, advice);
+                    } else {
+                        throw new RuntimeCamelException(
+                                String.format(
+                                        "The default constructor of the class %s is not accessible since it is in a package that is not opened to the extension.",
+                                        adviceClass.getName()));
+                    }
+                } catch (NoSuchMethodException e) {
+                    throw new RuntimeCamelException(
+                            String.format("Could not find the default constructor of the class %s.", adviceClass.getName()), e);
+                } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
+                    throw new RuntimeCamelException(e);
+                }
+            }
+        }
+
+        /**
+         * For all uris and ids extracted from {@link CamelMainTest#replaceRouteFromWith()}, replace the from endpoint
+         * in the route corresponding to the extracted id with the extracted uri knowing that the expected order is
+         * {@code route-id-1, new-uri-1, route-id-2, ...}.
+         * 
+         * @throws Exception if the content of {@link CamelMainTest#replaceRouteFromWith()} doesn't have the expected
+         *                   length or a route could not be advised.
+         */
+        private void applyReplaceRouteFromWith(ModelCamelContext context) throws Exception {
+            String[] fromEndpoints = annotation.replaceRouteFromWith();
+            if (fromEndpoints.length % 2 == 1) {
+                throw new RuntimeCamelException(
+                        "The length of the array of replaceRouteFromWith should be even, as we expect a route id followed by a uri");
+            }
+            for (int i = 0; i < fromEndpoints.length - 1; i += 2) {
+                final String uri = fromEndpoints[i + 1];
+                AdviceWith.adviceWith(context.getRouteDefinition(fromEndpoints[i]), context, new AdviceWithRouteBuilder() {
+                    @Override
+                    public void configure() {
+                        replaceFromWith(uri);
+                    }
+                });
+            }
+        }
+
+        /**
+         * For all methods or fields annotated with {@link ReplaceInRegistry}, replace in the registry the beans
+         * corresponding to their name and type.
+         * 
+         * @throws RuntimeCamelException if an annotated method could not be invoked or an annotated field cannot be
+         *                               accessed, or if the annotated method has parameters.
+         */
+        private void replaceBeansInRegistry(Registry registry) {
+            for (Method method : findAnnotatedMethods(requiredTestClass, ReplaceInRegistry.class,
+                    HierarchyTraversalMode.TOP_DOWN)) {
+                Class<?>[] parameterTypes = method.getParameterTypes();
+                if (parameterTypes.length == 0) {
+                    if (method.trySetAccessible()) {
+                        registry.bind(method.getName(), method.getReturnType(), invokeMethod(method, instance));
+                    } else {
+                        throw new RuntimeCamelException(
+                                String.format(
+                                        "The method %s is not accessible since it is in a package that is not opened to the extension.",
+                                        method.getName()));
+                    }
+                } else {
+                    throw new RuntimeCamelException(
+                            String.format("The method %s should not have any parameter.", method.getName()));
+                }
+            }
+            doWithFields(requiredTestClass, field -> {
+                if (field.isAnnotationPresent(ReplaceInRegistry.class)) {
+                    if (field.trySetAccessible()) {
+                        registry.bind(field.getName(), field.getType(), getField(field, instance));
+                    } else {
+                        throw new RuntimeCamelException(
+                                String.format(
+                                        "The field %s is not accessible since it is in a package that is not opened to the extension.",
+                                        field.getName()));
+                    }
+                }
+            });
+        }
+
+        /**
+         * Invoke all methods annotated with {@link Configure} that have one parameter of type
+         * {@link MainConfigurationProperties}.
+         * 
+         * @throws RuntimeCamelException if an annotated method could not be invoked or has invalid parameters.
+         */
+        private void invokeConfigureMethods(MainForTest main) {
+            for (Method method : findAnnotatedMethods(requiredTestClass, Configure.class, HierarchyTraversalMode.TOP_DOWN)) {
+                Class<?>[] parameterTypes = method.getParameterTypes();
+                if (parameterTypes.length == 1 && parameterTypes[0] == MainConfigurationProperties.class) {
+                    if (method.trySetAccessible()) {
+                        invokeMethod(method, instance, main.configure());
+                    } else {
+                        throw new RuntimeCamelException(
+                                String.format(
+                                        "The method %s is not accessible since it is in a package that is not opened to the extension.",
+                                        method.getName()));
+                    }
+                } else {
+                    throw new RuntimeCamelException(
+                            String.format(
+                                    "The method %s should have one single parameter of type MainConfigurationProperties.",
+                                    method.getName()));
+                }
+            }
+        }
+
+        /**
+         * Configures as override properties all the properties extracted from {@link CamelMainTest#properties()}
+         * knowing that the expected order is {@code property-key-1, property-value-1, property-key-2, ...}.
+         * 
+         * @throws RuntimeCamelException if the content of {@link CamelMainTest#properties()} doesn't have the expected
+         *                               length.
+         */
+        private void configureOverrideProperties(MainForTest main) {
+            String[] properties = annotation.properties();
+            if (properties.length % 2 == 1) {
+                throw new RuntimeCamelException(
+                        "The length of the array of properties should be even, as we expect a key followed by its value");
+            }
+            for (int i = 0; i < properties.length - 1; i += 2) {
+                main.addOverrideProperty(properties[i], properties[i + 1]);
+            }
+        }
+
+        /**
+         * Configure the property placeholder locations from the content of
+         * {@link CamelMainTest#propertyPlaceholderLocations()} if set otherwise from
+         * {@link CamelMainTest#propertyPlaceholderFileName()} if set in such way that the locations will be in the
+         * package of the test class or in the default package.
+         */
+        private void configurePropertyPlaceholderLocations(MainForTest main) {
+            String[] locations = annotation.propertyPlaceholderLocations();
+            if (locations.length == 0) {
+                String fileName = annotation.propertyPlaceholderFileName();
+                if (!fileName.isEmpty()) {
+                    main.setPropertyPlaceholderLocations(
+                            String.format("classpath:%s/%s;optional=true,classpath:%s;optional=true",
+                                    requiredTestClass.getPackageName().replace('.', '/'), fileName, fileName));
+                }
+            } else {
+                main.setPropertyPlaceholderLocations(String.join(",", locations));
+            }
+        }
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/CamelMainExtension.java b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/CamelMainExtension.java
new file mode 100644
index 0000000..2424dae
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/CamelMainExtension.java
@@ -0,0 +1,178 @@
+/*
+ * 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.main.junit5;
+
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.api.management.ManagedCamelContext;
+import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.test.CamelRouteCoverageDumper;
+import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.TimeUtils;
+import org.junit.jupiter.api.TestInstance;
+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.Extension;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.camel.test.junit5.CamelTestSupport.ROUTE_COVERAGE_ENABLED;
+import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.create;
+
+/**
+ * {@code CamelMainExtension} is a JUnit 5 extension meant to manage the lifecycle of a Camel context simulating a Camel
+ * Main application. This extension should not be called explicitly, a test class should only be annotated with the
+ * annotation {@link CamelMainTest} to trigger this extension.
+ *
+ * @see CamelMainTest
+ * @see AdviceRouteMapping
+ * @see Configure
+ * @see ReplaceInRegistry
+ * @see DebuggerCallback
+ */
+final class CamelMainExtension
+        implements Extension, BeforeEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback {
+
+    /**
+     * The logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(CamelMainExtension.class);
+    /**
+     * The namespace used to store the state of the test.
+     */
+    private static final ExtensionContext.Namespace NAMESPACE = create(CamelMainExtension.class);
+    /**
+     * The name of the key used to store the context of the test.
+     */
+    private static final String CONTEXT = "context";
+    /**
+     * The name of the key used to store the watch instance.
+     */
+    private static final String WATCH = "watch";
+    /**
+     * The utility class allowing to dump the route coverage of a given test.
+     */
+    private final CamelRouteCoverageDumper routeCoverageDumper = new CamelRouteCoverageDumper();
+
+    @Override
+    public void beforeEach(ExtensionContext context) throws Exception {
+        getContextStore(context).getOrComputeIfAbsent(CONTEXT, k -> createCamelMainContextAndStart(context));
+    }
+
+    @Override
+    public void beforeTestExecution(ExtensionContext context) {
+        context.getStore(NAMESPACE).put(WATCH, new StopWatch());
+        if (LOG.isInfoEnabled()) {
+            final Class<?> requiredTestClass = context.getRequiredTestClass();
+            final String currentTestName = context.getDisplayName();
+            LOG.info("********************************************************************************");
+            LOG.info("Testing: {} ({})", currentTestName, requiredTestClass.getName());
+            LOG.info("********************************************************************************");
+        }
+    }
+
+    @Override
+    public void afterTestExecution(ExtensionContext context) throws Exception {
+        final long time = context.getStore(NAMESPACE).remove(WATCH, StopWatch.class).taken();
+        final String currentTestName = context.getDisplayName();
+        if (LOG.isInfoEnabled()) {
+            final Class<?> requiredTestClass = context.getRequiredTestClass();
+            LOG.info("********************************************************************************");
+            LOG.info("Testing done: {} ({})", currentTestName, requiredTestClass.getName());
+            LOG.info("Took: {} ({} millis)", TimeUtils.printDuration(time), time);
+            LOG.info("********************************************************************************");
+        }
+        dumpRouteCoverageIfNeeded(context, time, currentTestName);
+    }
+
+    /**
+     * Create and start the Camel context simulating a Camel Main application based on what can be extracted from the
+     * given extension context.
+     */
+    private CamelMainContext createCamelMainContextAndStart(ExtensionContext context) {
+        try {
+            final CamelMainContext camelMainContext = CamelMainContext.builder(context)
+                    .useJmx(isRouteCoverageEnabled(context))
+                    .build();
+            camelMainContext.start();
+            return camelMainContext;
+        } catch (Exception e) {
+            throw new RuntimeCamelException(e);
+        }
+    }
+
+    /**
+     * @return the {@link ExtensionContext.Store} in which the {@link CamelMainContext} should be stored.
+     */
+    private ExtensionContext.Store getContextStore(ExtensionContext context) {
+        ExtensionContext sourceContext = context;
+        if (context.getTestInstanceLifecycle().stream()
+                .anyMatch(lifecycle -> lifecycle.equals(TestInstance.Lifecycle.PER_CLASS))) {
+            // In case it is per class get it from the parent context corresponding to the class context
+            sourceContext = context.getParent().orElseThrow();
+        }
+        return sourceContext.getStore(NAMESPACE);
+    }
+
+    /**
+     * Dump the route coverage for the given test if it is enabled.
+     */
+    private void dumpRouteCoverageIfNeeded(ExtensionContext context, long time, String currentTestName) throws Exception {
+        final Class<?> requiredTestClass = context.getRequiredTestClass();
+        final CamelMainTest annotation = requiredTestClass.getAnnotation(CamelMainTest.class);
+        // if we should dump route stats, then write that to a file
+        if (isRouteCoverageEnabled(annotation)) {
+            String className = requiredTestClass.getSimpleName();
+            String dir = "target/camel-route-coverage";
+            String name = String.format("%s-%s.xml", className, StringHelper.before(currentTestName, "("));
+
+            final ModelCamelContext camelContext = getContextStore(context).get(CONTEXT, CamelMainContext.class).context();
+            ManagedCamelContext mc = camelContext == null ? null : camelContext.getExtension(ManagedCamelContext.class);
+            ManagedCamelContextMBean managedCamelContext = mc == null ? null : mc.getManagedCamelContext();
+            if (managedCamelContext == null) {
+                LOG.warn("Cannot dump route coverage to file as JMX is not enabled. "
+                         + "Add camel-management JAR as dependency and/or override useJmx() method to enable JMX in the unit test classes.");
+            } else {
+                routeCoverageDumper.dump(managedCamelContext, camelContext, dir, name, requiredTestClass.getName(),
+                        currentTestName,
+                        time);
+            }
+        }
+    }
+
+    /**
+     * Indicates whether the route coverage is enabled according to the given extension context.
+     * 
+     * @return {@code true} if the route coverage is enabled, {@code false} otherwise.
+     */
+    private boolean isRouteCoverageEnabled(ExtensionContext context) {
+        return isRouteCoverageEnabled(context.getRequiredTestClass().getAnnotation(CamelMainTest.class));
+    }
+
+    /**
+     * Indicates whether the route coverage is enabled according to the given annotation and the value of the system
+     * property {@link org.apache.camel.test.junit5.CamelTestSupport#ROUTE_COVERAGE_ENABLED}.
+     * 
+     * @return {@code true} if the route coverage is enabled, {@code false} otherwise.
+     */
+    private boolean isRouteCoverageEnabled(CamelMainTest annotation) {
+        return "true".equalsIgnoreCase(System.getProperty(ROUTE_COVERAGE_ENABLED, "false")) || annotation.dumpRouteCoverage();
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/CamelMainTest.java b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/CamelMainTest.java
new file mode 100644
index 0000000..72ab2cc
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/CamelMainTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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.main.junit5;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.camel.CamelConfiguration;
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.PropertiesComponent;
+import org.apache.camel.support.EndpointHelper;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * {@code @CamelMainTest} is an annotation allowing to mark a test class as a test of a Camel Main application. A
+ * specific Camel context is created, initialized and started under the same conditions as a Camel Main application. The
+ * annotation can be inherited from a parent class.
+ * <p/>
+ * In the next example, the annotation {@code CamelMainTest} on the test class {@code SomeTest} indicates that the main
+ * class of the Camel Main application to simulate is {@code SomeMainClass} and has one additional configuration class
+ * to consider which is {@code SomeConfiguration}.
+ *
+ * <pre>
+ * <code>
+ *
+ * &#64;CamelMainTest(mainClass = SomeMainClass.class, configurationClasses = SomeConfiguration.class)
+ * class SomeTest {
+ *     // The rest of the test class
+ * }
+ * </code>
+ * </pre>
+ *
+ * @see AdviceRouteMapping
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@ExtendWith(CamelMainExtension.class)
+public @interface CamelMainTest {
+
+    /**
+     * Allows to specify the main class of the application to test if needed in order to simulate the same behavior as
+     * with {@link org.apache.camel.main.Main#Main(Class)}.
+     *
+     * @return the main class of the application to test if any. {@code void.class} by default indicating that there is
+     *         no specific main class.
+     */
+    Class<?> mainClass() default void.class;
+
+    /**
+     * Gives the list of properties to override with the Camel {@link PropertiesComponent}.
+     * <p/>
+     * In the next example, the annotation {@code CamelMainTest} on the test class {@code SomeTest} indicates the
+     * existing value of the property {@code host} is replaced with {@code localhost} and the existing value of the
+     * property {@code port} is replaced with {@code 8080}.
+     * 
+     * <pre>
+     * <code>
+     *
+     * &#64;CamelMainTest(properties = { "host", "localhost", "port", "8080" })
+     * class SomeTest {
+     *     // The rest of the test class
+     * }
+     * </code>
+     * </pre>
+     * 
+     * @return an array of {@code String} in the following order
+     *         {@code property-key-1, property-value-1, property-key-2, ...}
+     */
+    String[] properties() default {};
+
+    /**
+     * Gives the identifier of routes to advice by replacing their from endpoint with the corresponding URI.
+     * <p/>
+     * In the next example, the annotation {@code CamelMainTest} on the test class {@code SomeTest} indicates the value
+     * of the route whose identifier {@code main-route} is advised to replace its current from endpoint with a
+     * {@code direct:main} endpoint.
+     * 
+     * <pre>
+     * <code>
+     *
+     * &#64;CamelMainTest(replaceRouteFromWith = { "main-route", "direct:main" })
+     * class SomeTest {
+     *     // The rest of the test class
+     * }
+     * </code>
+     * </pre>
+     * 
+     * @return an array of {@code String} in the following order {@code route-id-1, new-uri-1, route-id-2, ...}
+     */
+    String[] replaceRouteFromWith() default {};
+
+    /**
+     * Gives the file name of the property placeholder to use for the test. This method assumes that the file is located
+     * either in the package of the test class or directly in the default package. In other words, if the test class is
+     * {@code com.company.SomeTest} and this method has been overridden to return {@code some-app.properties}, then it
+     * assumes that the actual possible locations of the property placeholder are
+     * {@code classpath:com/company/some-app.properties;optional=true,classpath:some-app.properties;optional=true} which
+     * means that for each property to find, it tries to get it first from the properties file of the same package if it
+     * exists and if it cannot be found, it tries to get it from the properties file with the same name but in the
+     * default package if it exists.
+     * <p>
+     * <b>Note:</b> Since the properties files are declared as optional, no exception is raised if they are both absent.
+     * <b>Note:</b> If {@link #propertyPlaceholderLocations()} is set, the value of this attribute is ignored.
+     *
+     * @return the file name of the property placeholder located in the same package as the test class or directly in
+     *         the default package. Not set by default.
+     */
+    String propertyPlaceholderFileName() default "";
+
+    /**
+     * Gives the property placeholder locations to use for the test. In case a property is defined at several property
+     * placeholder locations, the value of this property in the first property placeholder location according to the
+     * order in the list is used. In other words, the value of the properties defined in the first location of the
+     * property placeholder in the list takes precedence over the value of the properties of the following location of
+     * the property placeholder and so on.
+     * <p>
+     * <b>Note:</b> If this attribute is set, the value of {@link #propertyPlaceholderFileName()} is ignored.
+     *
+     * @return the property placeholder locations to use for the test.
+     */
+    String[] propertyPlaceholderLocations() default {};
+
+    /**
+     * Gives the additional camel configuration classes to add to the global configuration.
+     *
+     * @return an array of camel configuration classes.
+     */
+    Class<? extends CamelConfiguration>[] configurationClasses() default {};
+
+    /**
+     * Gives the mappings between the routes to advice and the corresponding route builders to call to advice the
+     * routes.
+     *
+     * @return an array of mapping between route and route builder
+     */
+    AdviceRouteMapping[] advices() default {};
+
+    /**
+     * Enable auto mocking endpoints based on the pattern.
+     * <p/>
+     * Return <tt>*</tt> to mock all endpoints.
+     *
+     * @see EndpointHelper#matchEndpoint(CamelContext, String, String)
+     */
+    String mockEndpoints() default "";
+
+    /**
+     * 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)
+     */
+    String mockEndpointsAndSkip() default "";
+
+    /**
+     * 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 a xml file in the <tt>target/camel-route-coverage</tt>
+     *         directory after the test has finished.
+     */
+    boolean dumpRouteCoverage() default false;
+
+    /**
+     * Returns the timeout to use when shutting down (unit in seconds).
+     * <p/>
+     * Will default use 10 seconds.
+     *
+     * @return the timeout to use
+     */
+    int shutdownTimeout() default 10;
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/CamelMainTestSupport.java b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/CamelMainTestSupport.java
index 1ea05ca..3d8dfab 100644
--- a/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/CamelMainTestSupport.java
+++ b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/CamelMainTestSupport.java
@@ -17,10 +17,8 @@
 package org.apache.camel.test.main.junit5;
 
 import org.apache.camel.CamelContext;
-import org.apache.camel.ProducerTemplate;
 import org.apache.camel.main.BaseMainSupport;
 import org.apache.camel.main.MainConfigurationProperties;
-import org.apache.camel.main.MainSupport;
 import org.apache.camel.spi.Registry;
 import org.apache.camel.test.junit5.CamelTestSupport;
 import org.slf4j.Logger;
@@ -91,7 +89,7 @@ public abstract class CamelMainTestSupport extends CamelTestSupport {
      * case it returns a non {@code null} value otherwise it uses the default property placeholder location
      * corresponding to the file {@code application.properties} available from the default package.
      *
-     * @return the location of the property placeholders to use for the test.
+     * @return the property placeholder locations to use for the test.
      */
     protected String getPropertyPlaceholderLocations() {
         final String locations = getPropertyPlaceholderLocationsFromFileName();
@@ -134,7 +132,8 @@ public abstract class CamelMainTestSupport extends CamelTestSupport {
     }
 
     @Override
-    protected void postProcessTest() throws Exception {
+    protected CamelContext createCamelContext() throws Exception {
+        final CamelContext context = super.createCamelContext();
         LOG.debug("Initialize the camel context as a Camel Main application");
         final MainForTest main = new MainForTest();
         final Class<?> mainClass = getMainClass();
@@ -144,8 +143,8 @@ public abstract class CamelMainTestSupport extends CamelTestSupport {
         configure(main.configure());
         main.setPropertyPlaceholderLocations(getPropertyPlaceholderLocations());
         main.setOverrideProperties(useOverridePropertiesWithPropertiesComponent());
-        main.init(context());
-        super.postProcessTest();
+        main.init(context);
+        return context;
     }
 
     @Override
@@ -167,31 +166,4 @@ public abstract class CamelMainTestSupport extends CamelTestSupport {
         return String.format("classpath:%s/%s;optional=true,classpath:%s;optional=true",
                 this.getClass().getPackageName().replace('.', '/'), location, location);
     }
-
-    /**
-     * An internal implementation of a {@link MainSupport} used to initialize the Camel context the same manner as a
-     * real Camel Main application.
-     */
-    private static class MainForTest extends MainSupport {
-
-        @Override
-        protected ProducerTemplate findOrCreateCamelTemplate() {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        protected CamelContext createCamelContext() {
-            throw new UnsupportedOperationException();
-        }
-
-        /**
-         * Initialize the given Camel context like a Camel Main application.
-         *
-         * @param  camelContext the Camel context to initialize.
-         * @throws Exception    if an error occurs while initializing the Camel context.
-         */
-        void init(CamelContext camelContext) throws Exception {
-            postProcessCamelContext(camelContext);
-        }
-    }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/Configure.java b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/Configure.java
new file mode 100644
index 0000000..f68eb18
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/Configure.java
@@ -0,0 +1,62 @@
+/*
+ * 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.main.junit5;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.camel.main.MainConfigurationProperties;
+
+/**
+ * {@code @Configure} is an annotation allowing to mark a method with an arbitrary name that is meant to be called to
+ * configure the Camel context to use for the test. Several methods in the test class and/or its parent classes can be
+ * annotated.
+ * <p/>
+ * Be aware that only a specific signature is supported, indeed any annotated methods must have only one parameter of
+ * type {@link MainConfigurationProperties}.
+ * <p/>
+ * In the next example, the annotation {@code Configure} on the method {@code configureRoutesBuilder} indicates the test
+ * framework to call it while initializing the Camel context used for the test.
+ *
+ * <pre>
+ * <code>
+ *
+ * &#64;CamelMainTest
+ * class SomeTest {
+ *
+ *     &#64;Configure
+ *     void configureRoutesBuilders(MainConfigurationProperties configuration) {
+ *         configuration.addRoutesBuilder(new SomeRouteBuilder());
+ *     }
+ *
+ *     // The rest of the test class
+ * }
+ * </code>
+ * </pre>
+ * 
+ * @see MainConfigurationProperties
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Configure {
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/DebuggerCallback.java b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/DebuggerCallback.java
new file mode 100644
index 0000000..35d70c6
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/DebuggerCallback.java
@@ -0,0 +1,41 @@
+/*
+ * 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.main.junit5;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.model.ProcessorDefinition;
+
+/**
+ * {@code DebuggerCallback} is an interface to implement in case a test wishes to be called <strong>immediately</strong>
+ * before and after invoking a processor by enabling and configuring automatically the debug mode.
+ */
+public interface DebuggerCallback {
+
+    /**
+     * Single step debugs and Camel invokes this method before entering the given processor
+     */
+    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
+     */
+    void debugAfter(
+            Exchange exchange, Processor processor, ProcessorDefinition<?> definition, String id, String label,
+            long timeTaken);
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/MyConfiguration.java b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/MainForTest.java
similarity index 51%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/MyConfiguration.java
copy to components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/MainForTest.java
index ba42287..cf252e4 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/MyConfiguration.java
+++ b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/MainForTest.java
@@ -16,25 +16,33 @@
  */
 package org.apache.camel.test.main.junit5;
 
-import org.apache.camel.BindToRegistry;
-import org.apache.camel.CamelConfiguration;
 import org.apache.camel.CamelContext;
-import org.apache.camel.PropertyInject;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.main.MainSupport;
 
 /**
- * Class to configure the Camel application.
+ * An internal implementation of a {@link MainSupport} used to initialize the Camel context the same manner as a real
+ * Camel Main application.
  */
-public class MyConfiguration implements CamelConfiguration {
+final class MainForTest extends MainSupport {
 
-    @BindToRegistry
-    public Greetings myGreetings(@PropertyInject("name") String name) {
-        // this will create an instance of this bean and bind it using the name of the method as name (eg myGreetings)
-        return new Greetings(name);
+    @Override
+    protected ProducerTemplate findOrCreateCamelTemplate() {
+        throw new UnsupportedOperationException();
     }
 
     @Override
-    public void configure(CamelContext camelContext) {
-        // this method is optional and can be removed if no additional configuration is needed.
+    protected CamelContext createCamelContext() {
+        throw new UnsupportedOperationException();
     }
 
+    /**
+     * Initialize the given Camel context like a Camel Main application.
+     *
+     * @param  camelContext the Camel context to initialize.
+     * @throws Exception    if an error occurs while initializing the Camel context.
+     */
+    void init(CamelContext camelContext) throws Exception {
+        postProcessCamelContext(camelContext);
+    }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/ReplaceInRegistry.java b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/ReplaceInRegistry.java
new file mode 100644
index 0000000..53e96b1
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/main/java/org/apache/camel/test/main/junit5/ReplaceInRegistry.java
@@ -0,0 +1,85 @@
+/*
+ * 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.main.junit5;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * {@code @ReplaceInRegistry} is annotation used to mark all the methods and fields that the test framework should
+ * consider replacing existing beans in the registry. It is meant to be used to replace a real implementation of a
+ * service with a mock or a test implementation.
+ * <p/>
+ * If a field is marked with the annotation {@code @ReplaceInRegistry}, the name and the type of the field are used to
+ * identify the bean to replace, and the value of the field is the new value of the bean. The field can be in the test
+ * class or in a parent class.
+ * <p/>
+ * In the next example, the annotation {@code ReplaceInRegistry} on the field {@code myGreetings} of type
+ * {@code Greetings} indicates that the bean with the same name and type should be replaced by an instance of
+ * {@code CustomGreetings}.
+ * 
+ * <pre>
+ * <code>
+ *
+ * &#64;CamelMainTest
+ * class SomeTest {
+ *
+ *     &#64;ReplaceInRegistry
+ *     Greetings myGreetings = new CustomGreetings("Willy");
+ *
+ *     // The rest of the test class
+ * }
+ * </code>
+ * </pre>
+ * <p/>
+ * If a method is marked with the annotation {@code @ReplaceInRegistry}, the name and the return type of the method are
+ * used to identify the bean to replace, and the return value of the method is the new value of the bean. The method can
+ * be in the test class or in a parent class.
+ * <p/>
+ * In the next example, the annotation {@code ReplaceInRegistry} on the method {@code myGreetings} whose return type is
+ * {@code Greetings} indicates that the bean with the same name and type should be replaced by an instance of
+ * {@code CustomGreetings}.
+ * 
+ * <pre>
+ * <code>
+ *
+ * &#64;CamelMainTest
+ * class SomeTest {
+ *
+ *     &#64;PropertyInject("name")
+ *     String name;
+ *
+ *     &#64;ReplaceInRegistry
+ *     Greetings myGreetings() {
+ *         return new CustomGreetings(name);
+ *     }
+ *
+ *     // The rest of the test class
+ * }
+ * </code>
+ * </pre>
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.FIELD })
+public @interface ReplaceInRegistry {
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/AdviceRouteTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/AdviceRouteTest.java
similarity index 60%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/AdviceRouteTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/AdviceRouteTest.java
index 398a7d9..2bcb167 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/AdviceRouteTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/AdviceRouteTest.java
@@ -14,11 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.annotation;
 
-import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.AdviceWithRouteBuilder;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.test.main.junit5.AdviceRouteMapping;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.Configure;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -26,14 +32,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 /**
  * A test class ensuring that a route can be advised.
  */
-class AdviceRouteTest extends CamelMainTestSupport {
+@CamelMainTest(advices = @AdviceRouteMapping(route = "foo", advice = AdviceRouteTest.TestBuilder.class))
+class AdviceRouteTest {
 
-    @Override
-    public boolean isUseAdviceWith() {
-        return true;
-    }
+    @EndpointInject("mock:out")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:foo")
+    ProducerTemplate template;
 
-    @Override
+    @Configure
     protected void configure(MainConfigurationProperties configuration) {
         // Add the configuration class
         configuration.addConfiguration(MyConfiguration.class);
@@ -41,15 +49,17 @@ class AdviceRouteTest extends CamelMainTestSupport {
 
     @Test
     void shouldAdviceTheRoute() throws Exception {
-        // Advice the route by replace the from endpoint
-        AdviceWith.adviceWith(context, "foo", ad -> ad.replaceFromWith("direct:foo"));
-
-        // must start Camel after we are done using advice-with
-        context.start();
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
         mock.expectedBodiesReceived("Hello Will!");
-        String result = template.requestBody("direct:foo", null, String.class);
+        String result = template.requestBody((Object) null, String.class);
         mock.assertIsSatisfied();
         assertEquals("Hello Will!", result);
     }
+
+    static class TestBuilder extends AdviceWithRouteBuilder {
+
+        @Override
+        public void configure() throws Exception {
+            replaceFromWith("direct:foo");
+        }
+    }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/AdviceRouteTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/DumpRouteCoverageTest.java
similarity index 61%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/AdviceRouteTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/DumpRouteCoverageTest.java
index 398a7d9..530ae1f 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/AdviceRouteTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/DumpRouteCoverageTest.java
@@ -14,41 +14,41 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.annotation;
 
-import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.Configure;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
- * A test class ensuring that a route can be advised.
+ * A test class ensuring that the route coverage can be dumped.
  */
-class AdviceRouteTest extends CamelMainTestSupport {
+@CamelMainTest(dumpRouteCoverage = true)
+class DumpRouteCoverageTest {
 
-    @Override
-    public boolean isUseAdviceWith() {
-        return true;
-    }
+    @EndpointInject("mock:out")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:in")
+    ProducerTemplate template;
 
-    @Override
-    protected void configure(MainConfigurationProperties configuration) {
+    @Configure
+    private void configure(MainConfigurationProperties configuration) { //NOPMD
         // Add the configuration class
         configuration.addConfiguration(MyConfiguration.class);
     }
 
     @Test
-    void shouldAdviceTheRoute() throws Exception {
-        // Advice the route by replace the from endpoint
-        AdviceWith.adviceWith(context, "foo", ad -> ad.replaceFromWith("direct:foo"));
-
-        // must start Camel after we are done using advice-with
-        context.start();
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
+    void shouldDumpTheRouteCoverage() throws Exception {
         mock.expectedBodiesReceived("Hello Will!");
-        String result = template.requestBody("direct:foo", null, String.class);
+        String result = template.requestBody((Object) null, String.class);
         mock.assertIsSatisfied();
         assertEquals("Hello Will!", result);
     }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/InheritanceTest.java
similarity index 64%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/InheritanceTest.java
index 123f860..3bcf657 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/InheritanceTest.java
@@ -14,32 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.annotation;
 
-import org.apache.camel.component.mock.MockEndpoint;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
- * A test class ensuring that the main class of the application to test can be specified.
+ * A test class ensuring that the annotation inheritance works as expected.
  */
-class WithMainClassTest extends CamelMainTestSupport {
-
-    @Override
-    protected Class<?> getMainClass() {
-        return MyMainClass.class;
-    }
+class InheritanceTest extends AdviceRouteTest {
 
     @Test
-    void shouldFindTheRouteBuilder() throws Exception {
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
+    void shouldInheritTheAnnotation() throws Exception {
         mock.expectedBodiesReceived("Hello Will!");
-        String result = template.requestBody("direct:in", null, String.class);
+        String result = template.requestBody((Object) null, String.class);
         mock.assertIsSatisfied();
         assertEquals("Hello Will!", result);
     }
-
-    static class MyMainClass {
-    }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/MockEndpointTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/MockEndpointTest.java
new file mode 100644
index 0000000..3967ac4
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/MockEndpointTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.main.junit5.annotation;
+
+import org.apache.camel.BeanInject;
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.Configure;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.component.mock.MockEndpoint.assertIsSatisfied;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * Test ensuring that endpoints matching with a pattern can be mocked.
+ */
+@CamelMainTest(mockEndpoints = "*")
+class MockEndpointTest {
+
+    @EndpointInject("mock:direct:start")
+    MockEndpoint mock1;
+    @EndpointInject("mock:direct:foo")
+    MockEndpoint mock2;
+    @EndpointInject("mock:log:foo")
+    MockEndpoint mock3;
+    @EndpointInject("mock:result")
+    MockEndpoint mock4;
+
+    @EndpointInject("direct:start")
+    ProducerTemplate template;
+
+    @BeanInject
+    CamelContext context;
+
+    @Configure
+    protected void configure(MainConfigurationProperties configuration) {
+        configuration.withAutoConfigurationEnabled(false);
+        configuration.addRoutesBuilder(new MyRouteBuilder());
+    }
+
+    @Test
+    void shouldMockEndpoints() throws Exception {
+        // notice we have automatically mocked all endpoints and the name of the
+        // endpoints is "mock:uri"
+        mock1.expectedBodiesReceived("Hello World");
+        mock2.expectedBodiesReceived("Hello World");
+        mock3.expectedBodiesReceived("Bye World");
+        mock4.expectedBodiesReceived("Bye World");
+
+        template.sendBody("Hello World");
+
+        assertIsSatisfied(context);
+
+        // 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"));
+    }
+
+    static class MyRouteBuilder extends 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/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/MockEndpointsAndSkipTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/MockEndpointsAndSkipTest.java
new file mode 100644
index 0000000..9e41716
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/MockEndpointsAndSkipTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.main.junit5.annotation;
+
+import org.apache.camel.BeanInject;
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.component.seda.SedaEndpoint;
+import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.Configure;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.component.mock.MockEndpoint.assertIsSatisfied;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test ensuring that endpoints matching with a pattern can be mocked and skipped.
+ */
+@CamelMainTest(mockEndpointsAndSkip = "direct:foo")
+class MockEndpointsAndSkipTest {
+
+    @EndpointInject("mock:result")
+    MockEndpoint mock1;
+    @EndpointInject("mock:direct:foo")
+    MockEndpoint mock2;
+
+    @EndpointInject("direct:start")
+    ProducerTemplate template;
+
+    @BeanInject
+    CamelContext context;
+
+    @Configure
+    protected void configure(MainConfigurationProperties configuration) {
+        configuration.withAutoConfigurationEnabled(false);
+        configuration.addRoutesBuilder(new MyRouteBuilder());
+    }
+
+    @Test
+    void shouldMockEndpoints() throws Exception {
+        // notice we have automatically mocked the direct:foo endpoints and the name
+        // of the endpoints is "mock:uri"
+        mock1.expectedBodiesReceived("Hello World");
+        mock2.expectedMessageCount(1);
+
+        template.sendBody("Hello World");
+
+        assertIsSatisfied(context);
+
+        // 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());
+    }
+
+    static class MyRouteBuilder extends 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");
+        }
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/OverridePropertiesTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/OverridePropertiesTest.java
similarity index 67%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/OverridePropertiesTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/OverridePropertiesTest.java
index b7b7cb2..2d3f7d1 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/OverridePropertiesTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/OverridePropertiesTest.java
@@ -14,39 +14,41 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
-
-import java.util.Properties;
+package org.apache.camel.test.main.junit5.annotation;
 
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.Configure;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
-import static org.apache.camel.util.PropertiesHelper.asProperties;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
  * Test ensuring that the default properties can be overridden.
  */
-class OverridePropertiesTest extends CamelMainTestSupport {
+@CamelMainTest(properties = { "name", "John" })
+class OverridePropertiesTest {
+
+    @EndpointInject("mock:out")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:in")
+    ProducerTemplate template;
 
-    @Override
-    protected void configure(MainConfigurationProperties configuration) {
+    @Configure
+    private void configure(MainConfigurationProperties configuration) {  //NOPMD
         // Add the configuration class
         configuration.addConfiguration(MyConfiguration.class);
     }
 
-    @Override
-    protected Properties useOverridePropertiesWithPropertiesComponent() {
-        return asProperties(
-                "name", "John");
-    }
-
     @Test
     void shouldOverrideDefaultProperties() throws Exception {
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
         mock.expectedBodiesReceived("Hello John!");
-        String result = template.requestBody("direct:in", null, String.class);
+        String result = template.requestBody((Object) null, String.class);
         mock.assertIsSatisfied();
         assertEquals("Hello John!", result);
     }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromFieldInheritanceTest.java
similarity index 75%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromFieldInheritanceTest.java
index 1bd5c52..a86c1af 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromFieldInheritanceTest.java
@@ -14,17 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.annotation;
 
-public class Greetings {
-
-    protected final String name;
-
-    public Greetings(String name) {
-        this.name = name;
-    }
+/**
+ * The test class ensuring that an existing bean can be replaced with a bean defined as a field in a parent class.
+ */
+class ReplaceBeanFromFieldInheritanceTest extends ReplaceBeanFromFieldTest {
 
-    public String sayHello() {
-        return String.format("Hello %s!", name);
-    }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceBeanTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromFieldTest.java
similarity index 60%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceBeanTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromFieldTest.java
index 3223268..881056c 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceBeanTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromFieldTest.java
@@ -14,42 +14,52 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.annotation;
 
-import org.apache.camel.PropertyInject;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
-import org.apache.camel.spi.Registry;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.Configure;
+import org.apache.camel.test.main.junit5.ReplaceInRegistry;
+import org.apache.camel.test.main.junit5.common.Greetings;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
- * The test class ensuring that an existing bean can be replaced with a bean bound from the test class.
+ * The test class ensuring that an existing bean can be replaced with a bean defined as a field.
  */
-class ReplaceBeanTest extends CamelMainTestSupport {
+@CamelMainTest
+class ReplaceBeanFromFieldTest {
 
-    @PropertyInject("name")
-    String name;
+    @EndpointInject("mock:out")
+    MockEndpoint mock;
 
-    @Override
+    @EndpointInject("direct:in")
+    ProducerTemplate template;
+
+    /**
+     * Replace the default bean whose name is <i>myGreetings</i> and type is {@link Greetings} with this custom
+     * implementation used for the test only.
+     */
+    @ReplaceInRegistry
+    Greetings myGreetings = new CustomGreetings("Willy");
+
+    @Configure
     protected void configure(MainConfigurationProperties configuration) {
         // Add the configuration class
         configuration.addConfiguration(MyConfiguration.class);
     }
 
-    @Override
-    protected void bindToRegistryAfterInjections(Registry registry) throws Exception {
-        registry.bind("myGreetings", Greetings.class, new CustomGreetings(name));
-    }
-
     @Test
     void shouldReplaceTheBeanWithACustomBean() throws Exception {
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
-        mock.expectedBodiesReceived("Hi Will!");
-        String result = template.requestBody("direct:in", null, String.class);
+        mock.expectedBodiesReceived("Hi Willy!");
+        String result = template.requestBody((Object) null, String.class);
         mock.assertIsSatisfied();
-        assertEquals("Hi Will!", result);
+        assertEquals("Hi Willy!", result);
     }
 
     static class CustomGreetings extends Greetings {
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromFieldWithMockTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromFieldWithMockTest.java
new file mode 100644
index 0000000..6998dcc
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromFieldWithMockTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.main.junit5.annotation;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.Configure;
+import org.apache.camel.test.main.junit5.ReplaceInRegistry;
+import org.apache.camel.test.main.junit5.common.Greetings;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.when;
+
+/**
+ * The test class ensuring that an existing bean can be replaced with a mock created by another extension.
+ */
+@ExtendWith(MockitoExtension.class)
+@CamelMainTest
+class ReplaceBeanFromFieldWithMockTest {
+
+    @EndpointInject("mock:out")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:in")
+    ProducerTemplate template;
+
+    /**
+     * Replace the default bean whose name is <i>myGreetings</i> and type is {@link Greetings} with a mock created by
+     * another extension.
+     */
+    @Mock
+    @ReplaceInRegistry
+    Greetings myGreetings;
+
+    @BeforeEach
+    void initMock() {
+        when(myGreetings.sayHello()).thenReturn("Hola Will!");
+    }
+
+    @Configure
+    protected void configure(MainConfigurationProperties configuration) {
+        // Add the configuration class
+        configuration.addConfiguration(MyConfiguration.class);
+    }
+
+    @Test
+    void shouldReplaceTheBeanWithAMock() throws Exception {
+        mock.expectedBodiesReceived("Hola Will!");
+        String result = template.requestBody((Object) null, String.class);
+        mock.assertIsSatisfied();
+        assertEquals("Hola Will!", result);
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromMethodInheritanceTest.java
similarity index 75%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromMethodInheritanceTest.java
index 1bd5c52..4fa4eb5 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromMethodInheritanceTest.java
@@ -14,17 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.annotation;
 
-public class Greetings {
-
-    protected final String name;
-
-    public Greetings(String name) {
-        this.name = name;
-    }
+/**
+ * The test class ensuring that an existing bean can be replaced with a bean created from a method in a parent class.
+ */
+class ReplaceBeanFromMethodInheritanceTest extends ReplaceBeanFromMethodTest {
 
-    public String sayHello() {
-        return String.format("Hello %s!", name);
-    }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceBeanTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromMethodTest.java
similarity index 64%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceBeanTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromMethodTest.java
index 3223268..0048b58 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceBeanTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromMethodTest.java
@@ -14,40 +14,56 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.annotation;
 
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
 import org.apache.camel.PropertyInject;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
-import org.apache.camel.spi.Registry;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.Configure;
+import org.apache.camel.test.main.junit5.ReplaceInRegistry;
+import org.apache.camel.test.main.junit5.common.Greetings;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
- * The test class ensuring that an existing bean can be replaced with a bean bound from the test class.
+ * The test class ensuring that an existing bean can be replaced with a bean created from a method.
  */
-class ReplaceBeanTest extends CamelMainTestSupport {
+@CamelMainTest
+class ReplaceBeanFromMethodTest {
 
     @PropertyInject("name")
     String name;
 
-    @Override
+    @EndpointInject("mock:out")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:in")
+    ProducerTemplate template;
+
+    @Configure
     protected void configure(MainConfigurationProperties configuration) {
         // Add the configuration class
         configuration.addConfiguration(MyConfiguration.class);
     }
 
-    @Override
-    protected void bindToRegistryAfterInjections(Registry registry) throws Exception {
-        registry.bind("myGreetings", Greetings.class, new CustomGreetings(name));
+    /**
+     * Replace the default bean whose name is <i>myGreetings</i> and type is {@link Greetings} with this custom
+     * implementation used for the test only.
+     */
+    @ReplaceInRegistry
+    Greetings myGreetings() {
+        return new CustomGreetings(name);
     }
 
     @Test
     void shouldReplaceTheBeanWithACustomBean() throws Exception {
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
         mock.expectedBodiesReceived("Hi Will!");
-        String result = template.requestBody("direct:in", null, String.class);
+        String result = template.requestBody((Object) null, String.class);
         mock.assertIsSatisfied();
         assertEquals("Hi Will!", result);
     }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromMethodWithMockTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromMethodWithMockTest.java
new file mode 100644
index 0000000..11fe383
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceBeanFromMethodWithMockTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.main.junit5.annotation;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.Configure;
+import org.apache.camel.test.main.junit5.ReplaceInRegistry;
+import org.apache.camel.test.main.junit5.common.Greetings;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.when;
+
+/**
+ * The test class ensuring that an existing bean can be replaced with a mock build manually in a method.
+ */
+@CamelMainTest
+class ReplaceBeanFromMethodWithMockTest {
+
+    @EndpointInject("mock:out")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:in")
+    ProducerTemplate template;
+
+    @Configure
+    protected void configure(MainConfigurationProperties configuration) {
+        // Add the configuration class
+        configuration.addConfiguration(MyConfiguration.class);
+    }
+
+    /**
+     * Replace the default bean whose name is <i>myGreetings</i> and type is {@link Greetings} with this custom
+     * implementation used for the test only.
+     */
+    @ReplaceInRegistry
+    Greetings myGreetings() {
+        final Greetings greetings = Mockito.mock(Greetings.class);
+        when(greetings.sayHello()).thenReturn("Hola Will!");
+        return greetings;
+    }
+
+    @Test
+    void shouldReplaceTheBeanWithAMock() throws Exception {
+        mock.expectedBodiesReceived("Hola Will!");
+        String result = template.requestBody((Object) null, String.class);
+        mock.assertIsSatisfied();
+        assertEquals("Hola Will!", result);
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceRouteFromTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceRouteFromTest.java
similarity index 67%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceRouteFromTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceRouteFromTest.java
index d3bb289..71f24aa 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceRouteFromTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/ReplaceRouteFromTest.java
@@ -14,30 +14,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.annotation;
 
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
-import org.apache.camel.test.junit5.CamelTestSupport;
-import org.junit.jupiter.api.BeforeEach;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.Configure;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
- * Test class ensuring that a from endpoint can be replaced by leveraging the method
- * {@link CamelTestSupport#replaceRouteFromWith(String, String)}.
+ * Test class ensuring that a from endpoint can be replaced.
  */
-class ReplaceRouteFromTest extends CamelMainTestSupport {
+@CamelMainTest(replaceRouteFromWith = { "foo", "direct:foo" })
+class ReplaceRouteFromTest {
 
-    @Override
-    @BeforeEach
-    public void setUp() throws Exception {
-        replaceRouteFromWith("foo", "direct:foo");
-        super.setUp();
-    }
+    @EndpointInject("mock:out")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:foo")
+    ProducerTemplate template;
 
-    @Override
+    @Configure
     protected void configure(MainConfigurationProperties configuration) {
         // Add the configuration class
         configuration.addConfiguration(MyConfiguration.class);
@@ -45,9 +47,8 @@ class ReplaceRouteFromTest extends CamelMainTestSupport {
 
     @Test
     void shouldReplaceTheFromEndpoint() throws Exception {
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
         mock.expectedBodiesReceived("Hello Will!");
-        String result = template.requestBody("direct:foo", null, String.class);
+        String result = template.requestBody((Object) null, String.class);
         mock.assertIsSatisfied();
         assertEquals("Hello Will!", result);
     }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/SupportParameterizedTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/SupportParameterizedTest.java
new file mode 100644
index 0000000..113b25c
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/SupportParameterizedTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.main.junit5.annotation;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.common.MyMainClass;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * The test class ensuring that a parameterized test is supported.
+ */
+@CamelMainTest(mainClass = MyMainClass.class)
+class SupportParameterizedTest {
+
+    @EndpointInject("mock:echo")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:echo")
+    ProducerTemplate template;
+
+    @AfterEach
+    void reset() {
+        mock.reset();
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = { "hello", "parameterized", "test" })
+    void shouldSupportMultipleCalls(String value) throws Exception {
+        mock.expectedBodiesReceived(value);
+        String result = template.requestBody((Object) value, String.class);
+        mock.assertIsSatisfied();
+        assertEquals(value, result);
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/SupportRepeatedTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/SupportRepeatedTest.java
new file mode 100644
index 0000000..e0fc3fc
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/SupportRepeatedTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.main.junit5.annotation;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.common.MyMainClass;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.RepeatedTest;
+import org.junit.jupiter.api.RepetitionInfo;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * The test class ensuring that a repeated test is supported.
+ */
+@CamelMainTest(mainClass = MyMainClass.class)
+class SupportRepeatedTest {
+
+    @EndpointInject("mock:echo")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:echo")
+    ProducerTemplate template;
+
+    @AfterEach
+    void reset() {
+        mock.reset();
+    }
+
+    @RepeatedTest(5)
+    void shouldSupportMultipleCalls(RepetitionInfo repetitionInfo) throws Exception {
+        assertEquals(5, repetitionInfo.getTotalRepetitions());
+        mock.expectedBodiesReceived(repetitionInfo.getCurrentRepetition());
+        int result = template.requestBody(repetitionInfo.getCurrentRepetition(), Integer.class);
+        mock.assertIsSatisfied();
+        assertEquals(repetitionInfo.getCurrentRepetition(), result);
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/TestInstanceDefaultTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/TestInstanceDefaultTest.java
new file mode 100644
index 0000000..dfef562
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/TestInstanceDefaultTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.main.junit5.annotation;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.common.MyMainClass;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * A test class ensuring that a new camel context is created for each test method.
+ */
+@CamelMainTest(mainClass = MyMainClass.class)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+class TestInstanceDefaultTest {
+
+    @EndpointInject("mock:bean")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:bean")
+    ProducerTemplate template;
+
+    @Order(1)
+    @Test
+    void shouldBeLaunchedFirst() throws Exception {
+        mock.expectedBodiesReceived(1);
+        int result = template.requestBody((Object) null, Integer.class);
+        mock.assertIsSatisfied();
+        assertEquals(1, result);
+    }
+
+    @Order(2)
+    @Test
+    void shouldBeLaunchedSecondWithSameResult() throws Exception {
+        mock.expectedBodiesReceived(1);
+        int result = template.requestBody((Object) null, Integer.class);
+        mock.assertIsSatisfied();
+        assertEquals(1, result);
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/TestInstancePerClassTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/TestInstancePerClassTest.java
new file mode 100644
index 0000000..bbe0cbe4
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/TestInstancePerClassTest.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.main.junit5.annotation;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.common.MyMainClass;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * A test class ensuring that a new camel context is created for the entire test class.
+ */
+@CamelMainTest(mainClass = MyMainClass.class)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+class TestInstancePerClassTest {
+
+    @EndpointInject("mock:bean")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:bean")
+    ProducerTemplate template;
+
+    @AfterEach
+    void reset() {
+        mock.reset();
+    }
+
+    @Order(1)
+    @Test
+    void shouldBeLaunchedFirst() throws Exception {
+        mock.expectedBodiesReceived(1);
+        int result = template.requestBody((Object) null, Integer.class);
+        mock.assertIsSatisfied();
+        assertEquals(1, result);
+    }
+
+    @Order(2)
+    @Test
+    void shouldBeLaunchedSecondWithDifferentResult() throws Exception {
+        mock.expectedBodiesReceived(2);
+        int result = template.requestBody((Object) null, Integer.class);
+        mock.assertIsSatisfied();
+        assertEquals(2, result);
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/TestInstancePerMethodTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/TestInstancePerMethodTest.java
new file mode 100644
index 0000000..70a9656
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/TestInstancePerMethodTest.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.main.junit5.annotation;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.common.MyMainClass;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * A test class ensuring that a new camel context is created for each test method.
+ */
+@CamelMainTest(mainClass = MyMainClass.class)
+@TestInstance(TestInstance.Lifecycle.PER_METHOD)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+class TestInstancePerMethodTest {
+
+    @EndpointInject("mock:bean")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:bean")
+    ProducerTemplate template;
+
+    @Order(1)
+    @Test
+    void shouldBeLaunchedFirst() throws Exception {
+        mock.expectedBodiesReceived(1);
+        int result = template.requestBody((Object) null, Integer.class);
+        mock.assertIsSatisfied();
+        assertEquals(1, result);
+    }
+
+    @Order(2)
+    @Test
+    void shouldBeLaunchedSecondWithSameResult() throws Exception {
+        mock.expectedBodiesReceived(1);
+        int result = template.requestBody((Object) null, Integer.class);
+        mock.assertIsSatisfied();
+        assertEquals(1, result);
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/UseSeveralPropertyPlaceholderLocationsTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/UseSeveralPropertyPlaceholderLocationsTest.java
similarity index 65%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/UseSeveralPropertyPlaceholderLocationsTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/UseSeveralPropertyPlaceholderLocationsTest.java
index 783ecb5..53c6894 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/UseSeveralPropertyPlaceholderLocationsTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/UseSeveralPropertyPlaceholderLocationsTest.java
@@ -14,10 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.annotation;
 
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.Configure;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -25,24 +30,25 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 /**
  * The test class ensuring that several property placeholder locations can be specified.
  */
-class UseSeveralPropertyPlaceholderLocationsTest extends CamelMainTestSupport {
+@CamelMainTest(propertyPlaceholderLocations = { "classpath:extra-application.properties", "classpath:application.properties" })
+class UseSeveralPropertyPlaceholderLocationsTest {
 
-    @Override
-    protected String getPropertyPlaceholderLocations() {
-        return "classpath:extra-application.properties,classpath:application.properties";
-    }
+    @EndpointInject("mock:out")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:in")
+    ProducerTemplate template;
 
-    @Override
-    protected void configure(MainConfigurationProperties configuration) {
+    @Configure
+    public void configure(MainConfigurationProperties configuration) {
         // Add the configuration class
         configuration.addConfiguration(MyConfiguration.class);
     }
 
     @Test
     void shouldApplyAllPropertyPlaceholderLocations() throws Exception {
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
         mock.expectedBodiesReceived("Hello Jack!");
-        String result = template.requestBody("direct:in", null, String.class);
+        String result = template.requestBody((Object) null, String.class);
         mock.assertIsSatisfied();
         assertEquals("Hello Jack!", result);
     }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/WithConfigurationClassTest.java
similarity index 62%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/WithConfigurationClassTest.java
index 123f860..4ad46ce 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/WithConfigurationClassTest.java
@@ -14,32 +14,34 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.annotation;
 
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
 import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
- * A test class ensuring that the main class of the application to test can be specified.
+ * Test ensuring that an additional configuration class can be specified.
  */
-class WithMainClassTest extends CamelMainTestSupport {
+@CamelMainTest(configurationClasses = MyConfiguration.class)
+class WithConfigurationClassTest {
 
-    @Override
-    protected Class<?> getMainClass() {
-        return MyMainClass.class;
-    }
+    @EndpointInject("mock:out")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:in")
+    ProducerTemplate template;
 
     @Test
-    void shouldFindTheRouteBuilder() throws Exception {
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
+    void shouldApplyConfigurationClass() throws Exception {
         mock.expectedBodiesReceived("Hello Will!");
-        String result = template.requestBody("direct:in", null, String.class);
+        String result = template.requestBody((Object) null, String.class);
         mock.assertIsSatisfied();
         assertEquals("Hello Will!", result);
     }
-
-    static class MyMainClass {
-    }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/WithDebuggerCallbackTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/WithDebuggerCallbackTest.java
new file mode 100644
index 0000000..99d417b
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/WithDebuggerCallbackTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.main.junit5.annotation;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.BeanInject;
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.DebuggerCallback;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * A test class ensuring that the debug mode is enabled if the test class implements the interface
+ * {@link DebuggerCallback}.
+ */
+@CamelMainTest(configurationClasses = MyConfiguration.class)
+class WithDebuggerCallbackTest implements DebuggerCallback {
+
+    @EndpointInject("direct:in")
+    ProducerTemplate template;
+
+    @BeanInject
+    CamelContext camelContext;
+
+    private final AtomicInteger counter = new AtomicInteger();
+
+    @Test
+    void shouldInvokeCallbackMethods() {
+        assertTrue(camelContext.isDebugging());
+        String result = template.requestBody((Object) null, String.class);
+        assertEquals("Hello Will!", result);
+        assertEquals(4, counter.get());
+    }
+
+    @Override
+    public void debugBefore(
+            Exchange exchange, Processor processor, ProcessorDefinition<?> definition, String id, String label) {
+        counter.incrementAndGet();
+    }
+
+    @Override
+    public void debugAfter(
+            Exchange exchange, Processor processor, ProcessorDefinition<?> definition, String id, String label,
+            long timeTaken) {
+        counter.incrementAndGet();
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/WithMainClassTest.java
similarity index 70%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/WithMainClassTest.java
index 123f860..83346e2 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/WithMainClassTest.java
@@ -14,9 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.annotation;
 
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
 import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.common.MyMainClass;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -24,22 +28,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 /**
  * A test class ensuring that the main class of the application to test can be specified.
  */
-class WithMainClassTest extends CamelMainTestSupport {
+@CamelMainTest(mainClass = MyMainClass.class)
+class WithMainClassTest {
 
-    @Override
-    protected Class<?> getMainClass() {
-        return MyMainClass.class;
-    }
+    @EndpointInject("mock:out")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:in")
+    ProducerTemplate template;
 
     @Test
     void shouldFindTheRouteBuilder() throws Exception {
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
         mock.expectedBodiesReceived("Hello Will!");
-        String result = template.requestBody("direct:in", null, String.class);
+        String result = template.requestBody((Object) null, String.class);
         mock.assertIsSatisfied();
         assertEquals("Hello Will!", result);
     }
-
-    static class MyMainClass {
-    }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/custom/LoadCustomConfigurationDefaultPackageTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/custom/LoadCustomConfigurationDefaultPackageTest.java
similarity index 71%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/custom/LoadCustomConfigurationDefaultPackageTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/custom/LoadCustomConfigurationDefaultPackageTest.java
index 77bf863..5c20ce7 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/custom/LoadCustomConfigurationDefaultPackageTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/custom/LoadCustomConfigurationDefaultPackageTest.java
@@ -14,12 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5.custom;
+package org.apache.camel.test.main.junit5.annotation.custom;
 
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
-import org.apache.camel.test.main.junit5.CamelMainTestSupport;
-import org.apache.camel.test.main.junit5.MyConfiguration;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.Configure;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -28,14 +31,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
  * The test class ensuring that a custom property placeholder can be specified with the name of the file located in the
  * default package.
  */
-class LoadCustomConfigurationDefaultPackageTest extends CamelMainTestSupport {
+@CamelMainTest(propertyPlaceholderFileName = "custom-default-package-application.properties")
+class LoadCustomConfigurationDefaultPackageTest {
 
-    @Override
-    protected String getPropertyPlaceholderFileName() {
-        return "custom-default-package-application.properties";
-    }
+    @EndpointInject("mock:out")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:in")
+    ProducerTemplate template;
 
-    @Override
+    @Configure
     protected void configure(MainConfigurationProperties configuration) {
         // Add the configuration class
         configuration.addConfiguration(MyConfiguration.class);
@@ -45,9 +50,8 @@ class LoadCustomConfigurationDefaultPackageTest extends CamelMainTestSupport {
 
     @Test
     void shouldFindCustomConfiguration() throws Exception {
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
         mock.expectedBodiesReceived("Hello Johnny!");
-        String result = template.requestBody("direct:in", null, String.class);
+        String result = template.requestBody((Object) null, String.class);
         mock.assertIsSatisfied();
         assertEquals("Hello Johnny!", result);
     }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/custom/LoadCustomConfigurationSamePackageTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/custom/LoadCustomConfigurationSamePackageTest.java
similarity index 68%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/custom/LoadCustomConfigurationSamePackageTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/custom/LoadCustomConfigurationSamePackageTest.java
index 06a0019..3896776 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/custom/LoadCustomConfigurationSamePackageTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/annotation/custom/LoadCustomConfigurationSamePackageTest.java
@@ -14,12 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5.custom;
+package org.apache.camel.test.main.junit5.annotation.custom;
 
+import org.apache.camel.EndpointInject;
+import org.apache.camel.ProducerTemplate;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
-import org.apache.camel.test.main.junit5.CamelMainTestSupport;
-import org.apache.camel.test.main.junit5.MyConfiguration;
+import org.apache.camel.test.main.junit5.CamelMainTest;
+import org.apache.camel.test.main.junit5.Configure;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -28,15 +31,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
  * The test class ensuring that a custom property placeholder can be specified with the name of the file located in the
  * same package.
  */
-class LoadCustomConfigurationSamePackageTest extends CamelMainTestSupport {
+@CamelMainTest(propertyPlaceholderFileName = "custom-same-package-application.properties")
+class LoadCustomConfigurationSamePackageTest {
 
-    @Override
-    protected String getPropertyPlaceholderFileName() {
-        return "custom-same-package-application.properties";
-    }
+    @EndpointInject("mock:out")
+    MockEndpoint mock;
+
+    @EndpointInject("direct:in")
+    ProducerTemplate template;
 
-    @Override
-    protected void configure(MainConfigurationProperties configuration) {
+    @Configure
+    void configure(MainConfigurationProperties configuration) {
         // Add the configuration class
         configuration.addConfiguration(MyConfiguration.class);
         // Add all the XML routes
@@ -45,9 +50,8 @@ class LoadCustomConfigurationSamePackageTest extends CamelMainTestSupport {
 
     @Test
     void shouldFindCustomConfiguration() throws Exception {
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
         mock.expectedBodiesReceived("Hello John!");
-        String result = template.requestBody("direct:in", null, String.class);
+        String result = template.requestBody((Object) null, String.class);
         mock.assertIsSatisfied();
         assertEquals("Hello John!", result);
     }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/EchoRouteBuilder.java
similarity index 76%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/EchoRouteBuilder.java
index 1bd5c52..d2b5c23 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/EchoRouteBuilder.java
@@ -14,17 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.common;
 
-public class Greetings {
+import org.apache.camel.builder.RouteBuilder;
 
-    protected final String name;
+public class EchoRouteBuilder extends RouteBuilder {
 
-    public Greetings(String name) {
-        this.name = name;
-    }
-
-    public String sayHello() {
-        return String.format("Hello %s!", name);
+    @Override
+    public void configure() throws Exception {
+        from("direct:echo").to("mock:echo");
     }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/Greetings.java
similarity index 95%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/Greetings.java
index 1bd5c52..19d7549 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/Greetings.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.common;
 
 public class Greetings {
 
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/MyConfiguration.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/MyConfiguration.java
similarity index 96%
rename from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/MyConfiguration.java
rename to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/MyConfiguration.java
index ba42287..6109ecf 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/MyConfiguration.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/MyConfiguration.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.common;
 
 import org.apache.camel.BindToRegistry;
 import org.apache.camel.CamelConfiguration;
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/MyMainClass.java
similarity index 75%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/MyMainClass.java
index 1bd5c52..7b40780 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/MyMainClass.java
@@ -14,17 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.common;
 
-public class Greetings {
-
-    protected final String name;
-
-    public Greetings(String name) {
-        this.name = name;
-    }
-
-    public String sayHello() {
-        return String.format("Hello %s!", name);
-    }
+public class MyMainClass {
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/MySatefulRouteBuilder.java
similarity index 73%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/MySatefulRouteBuilder.java
index 1bd5c52..913a217 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/MySatefulRouteBuilder.java
@@ -14,17 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.common;
 
-public class Greetings {
+import org.apache.camel.builder.RouteBuilder;
 
-    protected final String name;
+public class MySatefulRouteBuilder extends RouteBuilder {
 
-    public Greetings(String name) {
-        this.name = name;
-    }
-
-    public String sayHello() {
-        return String.format("Hello %s!", name);
+    @Override
+    public void configure() throws Exception {
+        from("direct:bean").bean(new MyStatefulBean()).to("mock:bean");
     }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/MyStatefulBean.java
similarity index 75%
rename from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
rename to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/MyStatefulBean.java
index 1bd5c52..19a4c4a 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/Greetings.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/common/MyStatefulBean.java
@@ -14,17 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.common;
 
-public class Greetings {
+import java.util.concurrent.atomic.AtomicInteger;
 
-    protected final String name;
+public class MyStatefulBean {
+    private final AtomicInteger counter = new AtomicInteger();
 
-    public Greetings(String name) {
-        this.name = name;
-    }
-
-    public String sayHello() {
-        return String.format("Hello %s!", name);
+    public int add() {
+        return counter.incrementAndGet();
     }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/AdviceRouteTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/AdviceRouteTest.java
similarity index 91%
rename from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/AdviceRouteTest.java
rename to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/AdviceRouteTest.java
index 398a7d9..1100724 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/AdviceRouteTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/AdviceRouteTest.java
@@ -14,11 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.legacy;
 
 import org.apache.camel.builder.AdviceWith;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.test.main.junit5.CamelMainTestSupport;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/OverridePropertiesTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/OverridePropertiesTest.java
similarity index 91%
rename from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/OverridePropertiesTest.java
rename to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/OverridePropertiesTest.java
index b7b7cb2..762585a 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/OverridePropertiesTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/OverridePropertiesTest.java
@@ -14,12 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.legacy;
 
 import java.util.Properties;
 
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.test.main.junit5.CamelMainTestSupport;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.apache.camel.util.PropertiesHelper.asProperties;
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceBeanTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/ReplaceBeanTest.java
similarity index 90%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceBeanTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/ReplaceBeanTest.java
index 3223268..e7338c6 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceBeanTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/ReplaceBeanTest.java
@@ -14,12 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.legacy;
 
 import org.apache.camel.PropertyInject;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
 import org.apache.camel.spi.Registry;
+import org.apache.camel.test.main.junit5.CamelMainTestSupport;
+import org.apache.camel.test.main.junit5.common.Greetings;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceBeanTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/ReplaceBeanWithMockTest.java
similarity index 65%
rename from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceBeanTest.java
rename to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/ReplaceBeanWithMockTest.java
index 3223268..690ab63 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceBeanTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/ReplaceBeanWithMockTest.java
@@ -14,23 +14,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.legacy;
 
-import org.apache.camel.PropertyInject;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
 import org.apache.camel.spi.Registry;
+import org.apache.camel.test.main.junit5.CamelMainTestSupport;
+import org.apache.camel.test.main.junit5.common.Greetings;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.when;
 
 /**
- * The test class ensuring that an existing bean can be replaced with a bean bound from the test class.
+ * The test class ensuring that an existing bean can be replaced with a mock from another extension.
  */
-class ReplaceBeanTest extends CamelMainTestSupport {
+@ExtendWith(MockitoExtension.class)
+class ReplaceBeanWithMockTest extends CamelMainTestSupport {
 
-    @PropertyInject("name")
-    String name;
+    @Mock
+    Greetings greetings;
 
     @Override
     protected void configure(MainConfigurationProperties configuration) {
@@ -40,27 +47,16 @@ class ReplaceBeanTest extends CamelMainTestSupport {
 
     @Override
     protected void bindToRegistryAfterInjections(Registry registry) throws Exception {
-        registry.bind("myGreetings", Greetings.class, new CustomGreetings(name));
+        when(greetings.sayHello()).thenReturn("Hola Will!");
+        registry.bind("myGreetings", Greetings.class, greetings);
     }
 
     @Test
-    void shouldReplaceTheBeanWithACustomBean() throws Exception {
+    void shouldReplaceTheBeanWithAMock() throws Exception {
         MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
-        mock.expectedBodiesReceived("Hi Will!");
+        mock.expectedBodiesReceived("Hola Will!");
         String result = template.requestBody("direct:in", null, String.class);
         mock.assertIsSatisfied();
-        assertEquals("Hi Will!", result);
-    }
-
-    static class CustomGreetings extends Greetings {
-
-        public CustomGreetings(String name) {
-            super(name);
-        }
-
-        @Override
-        public String sayHello() {
-            return String.format("Hi %s!", name);
-        }
+        assertEquals("Hola Will!", result);
     }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceRouteFromTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/ReplaceRouteFromTest.java
similarity index 91%
rename from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceRouteFromTest.java
rename to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/ReplaceRouteFromTest.java
index d3bb289..14ec365 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/ReplaceRouteFromTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/ReplaceRouteFromTest.java
@@ -14,11 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.legacy;
 
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
 import org.apache.camel.test.junit5.CamelTestSupport;
+import org.apache.camel.test.main.junit5.CamelMainTestSupport;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/SupportParameterizedTest.java
similarity index 56%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/SupportParameterizedTest.java
index 123f860..467766e 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/SupportParameterizedTest.java
@@ -14,32 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.legacy;
 
 import org.apache.camel.component.mock.MockEndpoint;
-import org.junit.jupiter.api.Test;
+import org.apache.camel.test.main.junit5.CamelMainTestSupport;
+import org.apache.camel.test.main.junit5.common.MyMainClass;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
- * A test class ensuring that the main class of the application to test can be specified.
+ * The test class ensuring that a parameterized test is supported.
  */
-class WithMainClassTest extends CamelMainTestSupport {
+class SupportParameterizedTest extends CamelMainTestSupport {
 
     @Override
     protected Class<?> getMainClass() {
         return MyMainClass.class;
     }
 
-    @Test
-    void shouldFindTheRouteBuilder() throws Exception {
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
-        mock.expectedBodiesReceived("Hello Will!");
-        String result = template.requestBody("direct:in", null, String.class);
+    @ParameterizedTest
+    @ValueSource(strings = { "hello", "parameterized", "test" })
+    void shouldSupportMultipleCalls(String value) throws Exception {
+        MockEndpoint mock = context.getEndpoint("mock:echo", MockEndpoint.class);
+        mock.expectedBodiesReceived(value);
+        String result = template.requestBody("direct:echo", value, String.class);
         mock.assertIsSatisfied();
-        assertEquals("Hello Will!", result);
-    }
-
-    static class MyMainClass {
+        assertEquals(value, result);
     }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/SupportRepeatedTest.java
similarity index 54%
copy from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java
copy to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/SupportRepeatedTest.java
index 123f860..fad3fc6 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/SupportRepeatedTest.java
@@ -14,32 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.legacy;
 
 import org.apache.camel.component.mock.MockEndpoint;
-import org.junit.jupiter.api.Test;
+import org.apache.camel.test.main.junit5.CamelMainTestSupport;
+import org.apache.camel.test.main.junit5.common.MyMainClass;
+import org.junit.jupiter.api.RepeatedTest;
+import org.junit.jupiter.api.RepetitionInfo;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
- * A test class ensuring that the main class of the application to test can be specified.
+ * The test class ensuring that a repeated test is supported.
  */
-class WithMainClassTest extends CamelMainTestSupport {
+class SupportRepeatedTest extends CamelMainTestSupport {
 
     @Override
     protected Class<?> getMainClass() {
         return MyMainClass.class;
     }
 
-    @Test
-    void shouldFindTheRouteBuilder() throws Exception {
-        MockEndpoint mock = context.getEndpoint("mock:out", MockEndpoint.class);
-        mock.expectedBodiesReceived("Hello Will!");
-        String result = template.requestBody("direct:in", null, String.class);
+    @RepeatedTest(5)
+    void shouldSupportMultipleCalls(RepetitionInfo repetitionInfo) throws Exception {
+        assertEquals(5, repetitionInfo.getTotalRepetitions());
+        MockEndpoint mock = context.getEndpoint("mock:echo", MockEndpoint.class);
+        mock.expectedBodiesReceived(repetitionInfo.getCurrentRepetition());
+        int result = template.requestBody("direct:echo", repetitionInfo.getCurrentRepetition(), Integer.class);
         mock.assertIsSatisfied();
-        assertEquals("Hello Will!", result);
-    }
-
-    static class MyMainClass {
+        assertEquals(repetitionInfo.getCurrentRepetition(), result);
     }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/TestInstanceDefaultTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/TestInstanceDefaultTest.java
new file mode 100644
index 0000000..4aa49c9
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/TestInstanceDefaultTest.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.main.junit5.legacy;
+
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.main.junit5.CamelMainTestSupport;
+import org.apache.camel.test.main.junit5.common.MyMainClass;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * A test class ensuring that a new camel context is created for each test method.
+ */
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+class TestInstanceDefaultTest extends CamelMainTestSupport {
+
+    @Override
+    protected Class<?> getMainClass() {
+        return MyMainClass.class;
+    }
+
+    @Order(1)
+    @Test
+    void shouldBeLaunchedFirst() throws Exception {
+        MockEndpoint mock = context.getEndpoint("mock:bean", MockEndpoint.class);
+        mock.expectedBodiesReceived(1);
+        int result = template.requestBody("direct:bean", null, Integer.class);
+        mock.assertIsSatisfied();
+        assertEquals(1, result);
+    }
+
+    @Order(2)
+    @Test
+    void shouldBeLaunchedSecondWithSameResult() throws Exception {
+        MockEndpoint mock = context.getEndpoint("mock:bean", MockEndpoint.class);
+        mock.expectedBodiesReceived(1);
+        int result = template.requestBody("direct:bean", null, Integer.class);
+        mock.assertIsSatisfied();
+        assertEquals(1, result);
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/TestInstancePerClassTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/TestInstancePerClassTest.java
new file mode 100644
index 0000000..d0df37f
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/TestInstancePerClassTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.main.junit5.legacy;
+
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.main.junit5.CamelMainTestSupport;
+import org.apache.camel.test.main.junit5.common.MyMainClass;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * A test class ensuring that a new camel context is created for the entire test class.
+ */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+class TestInstancePerClassTest extends CamelMainTestSupport {
+
+    @Override
+    protected Class<?> getMainClass() {
+        return MyMainClass.class;
+    }
+
+    @Order(1)
+    @Test
+    void shouldBeLaunchedFirst() throws Exception {
+        MockEndpoint mock = context.getEndpoint("mock:bean", MockEndpoint.class);
+        mock.expectedBodiesReceived(1);
+        int result = template.requestBody("direct:bean", null, Integer.class);
+        mock.assertIsSatisfied();
+        assertEquals(1, result);
+    }
+
+    @Order(2)
+    @Test
+    void shouldBeLaunchedSecondWithDifferentResult() throws Exception {
+        MockEndpoint mock = context.getEndpoint("mock:bean", MockEndpoint.class);
+        mock.expectedBodiesReceived(2);
+        int result = template.requestBody("direct:bean", null, Integer.class);
+        mock.assertIsSatisfied();
+        assertEquals(2, result);
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/TestInstancePerMethodTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/TestInstancePerMethodTest.java
new file mode 100644
index 0000000..fa5873f
--- /dev/null
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/TestInstancePerMethodTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.main.junit5.legacy;
+
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.main.junit5.CamelMainTestSupport;
+import org.apache.camel.test.main.junit5.common.MyMainClass;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * A test class ensuring that a new camel context is created for each test method.
+ */
+@TestInstance(TestInstance.Lifecycle.PER_METHOD)
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+class TestInstancePerMethodTest extends CamelMainTestSupport {
+
+    @Override
+    protected Class<?> getMainClass() {
+        return MyMainClass.class;
+    }
+
+    @Order(1)
+    @Test
+    void shouldBeLaunchedFirst() throws Exception {
+        MockEndpoint mock = context.getEndpoint("mock:bean", MockEndpoint.class);
+        mock.expectedBodiesReceived(1);
+        int result = template.requestBody("direct:bean", null, Integer.class);
+        mock.assertIsSatisfied();
+        assertEquals(1, result);
+    }
+
+    @Order(2)
+    @Test
+    void shouldBeLaunchedSecondWithSameResult() throws Exception {
+        MockEndpoint mock = context.getEndpoint("mock:bean", MockEndpoint.class);
+        mock.expectedBodiesReceived(1);
+        int result = template.requestBody("direct:bean", null, Integer.class);
+        mock.assertIsSatisfied();
+        assertEquals(1, result);
+    }
+}
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/UseSeveralPropertyPlaceholderLocationsTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/UseSeveralPropertyPlaceholderLocationsTest.java
similarity index 91%
rename from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/UseSeveralPropertyPlaceholderLocationsTest.java
rename to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/UseSeveralPropertyPlaceholderLocationsTest.java
index 783ecb5..65cbebf 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/UseSeveralPropertyPlaceholderLocationsTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/UseSeveralPropertyPlaceholderLocationsTest.java
@@ -14,10 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.legacy;
 
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
+import org.apache.camel.test.main.junit5.CamelMainTestSupport;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/WithMainClassTest.java
similarity index 90%
rename from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java
rename to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/WithMainClassTest.java
index 123f860..16b5b5c 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/WithMainClassTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/WithMainClassTest.java
@@ -14,9 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5;
+package org.apache.camel.test.main.junit5.legacy;
 
 import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.main.junit5.CamelMainTestSupport;
+import org.apache.camel.test.main.junit5.common.MyMainClass;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -39,7 +41,4 @@ class WithMainClassTest extends CamelMainTestSupport {
         mock.assertIsSatisfied();
         assertEquals("Hello Will!", result);
     }
-
-    static class MyMainClass {
-    }
 }
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/custom/LoadCustomConfigurationDefaultPackageTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/custom/LoadCustomConfigurationDefaultPackageTest.java
similarity index 94%
rename from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/custom/LoadCustomConfigurationDefaultPackageTest.java
rename to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/custom/LoadCustomConfigurationDefaultPackageTest.java
index 77bf863..884a178 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/custom/LoadCustomConfigurationDefaultPackageTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/custom/LoadCustomConfigurationDefaultPackageTest.java
@@ -14,12 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5.custom;
+package org.apache.camel.test.main.junit5.legacy.custom;
 
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
 import org.apache.camel.test.main.junit5.CamelMainTestSupport;
-import org.apache.camel.test.main.junit5.MyConfiguration;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/custom/LoadCustomConfigurationSamePackageTest.java b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/custom/LoadCustomConfigurationSamePackageTest.java
similarity index 94%
rename from components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/custom/LoadCustomConfigurationSamePackageTest.java
rename to components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/custom/LoadCustomConfigurationSamePackageTest.java
index 06a0019..ed66965 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/custom/LoadCustomConfigurationSamePackageTest.java
+++ b/components/camel-test/camel-test-main-junit5/src/test/java/org/apache/camel/test/main/junit5/legacy/custom/LoadCustomConfigurationSamePackageTest.java
@@ -14,12 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.test.main.junit5.custom;
+package org.apache.camel.test.main.junit5.legacy.custom;
 
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.main.MainConfigurationProperties;
 import org.apache.camel.test.main.junit5.CamelMainTestSupport;
-import org.apache.camel.test.main.junit5.MyConfiguration;
+import org.apache.camel.test.main.junit5.common.MyConfiguration;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
diff --git a/components/camel-test/camel-test-main-junit5/src/test/resources/org/apache/camel/test/main/junit5/custom/custom-same-package-application.properties b/components/camel-test/camel-test-main-junit5/src/test/resources/org/apache/camel/test/main/junit5/annotation/custom/custom-same-package-application.properties
similarity index 100%
copy from components/camel-test/camel-test-main-junit5/src/test/resources/org/apache/camel/test/main/junit5/custom/custom-same-package-application.properties
copy to components/camel-test/camel-test-main-junit5/src/test/resources/org/apache/camel/test/main/junit5/annotation/custom/custom-same-package-application.properties
diff --git a/components/camel-test/camel-test-main-junit5/src/test/resources/org/apache/camel/test/main/junit5/custom/custom-same-package-application.properties b/components/camel-test/camel-test-main-junit5/src/test/resources/org/apache/camel/test/main/junit5/legacy/custom/custom-same-package-application.properties
similarity index 100%
rename from components/camel-test/camel-test-main-junit5/src/test/resources/org/apache/camel/test/main/junit5/custom/custom-same-package-application.properties
rename to components/camel-test/camel-test-main-junit5/src/test/resources/org/apache/camel/test/main/junit5/legacy/custom/custom-same-package-application.properties
diff --git a/components/camel-test/camel-test-main-junit5/src/test/resources/routes/my-route.xml b/components/camel-test/camel-test-main-junit5/src/test/resources/routes/my-route.xml
index e615854..f3d6cd7 100644
--- a/components/camel-test/camel-test-main-junit5/src/test/resources/routes/my-route.xml
+++ b/components/camel-test/camel-test-main-junit5/src/test/resources/routes/my-route.xml
@@ -20,7 +20,7 @@
 <routes id="camel" xmlns="http://camel.apache.org/schema/spring">
     <route id="foo">
         <from uri="direct:in"/>
-        <bean ref="myGreetings"/>
+        <bean ref="myGreetings" method="sayHello"/>
         <to uri="mock:out"/>
     </route>
 </routes>