You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2021/03/12 07:35:20 UTC
[camel] 02/08: CAMEL-16334 - Having a middle folder for test (unit
+ testcontainers) components
This is an automated email from the ASF dual-hosted git repository.
acosentino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git
commit e342a0e4a126337487ac2283e93571d1ab0ab67e
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Fri Mar 12 07:13:32 2021 +0100
CAMEL-16334 - Having a middle folder for test (unit + testcontainers) components
---
components/camel-test/camel-test-cdi/pom.xml | 68 ++
.../services/org/apache/camel/other.properties | 7 +
.../src/generated/resources/test-cdi.json | 15 +
.../camel-test-cdi/src/main/docs/test-cdi.adoc | 738 +++++++++++++
.../apache/camel/test/cdi/AnnotatedDecorator.java | 84 ++
.../camel/test/cdi/AnnotatedMethodDecorator.java | 71 ++
.../camel/test/cdi/AnnotatedTypeDecorator.java | 80 ++
.../java/org/apache/camel/test/cdi/AnyLiteral.java | 30 +
.../main/java/org/apache/camel/test/cdi/Beans.java | 97 ++
.../org/apache/camel/test/cdi/CamelCdiContext.java | 46 +
.../apache/camel/test/cdi/CamelCdiDeployment.java | 82 ++
.../org/apache/camel/test/cdi/CamelCdiRunner.java | 98 ++
.../camel/test/cdi/CamelCdiTestExtension.java | 71 ++
.../test/cdi/FrameworkAnnotatedParameter.java | 91 ++
.../test/cdi/FrameworkMethodCdiInjection.java | 53 +
.../test/cdi/FrameworkMethodInjectionPoint.java | 88 ++
.../camel/test/cdi/FrameworkMethodSorter.java | 37 +
.../main/java/org/apache/camel/test/cdi/Order.java | 32 +
.../org/apache/camel/test/cdi/PriorityLiteral.java | 38 +
.../java/org/apache/camel/test/cdi/FilterTest.java | 73 ++
.../src/test/resources/log4j2.properties | 28 +
components/camel-test/camel-test-junit5/pom.xml | 170 +++
.../services/org/apache/camel/other.properties | 7 +
.../src/generated/resources/test-junit5.json | 15 +
.../src/main/docs/test-junit5.adoc | 93 ++
.../org/apache/camel/test/AvailablePortFinder.java | 93 ++
.../AvailablePortFinderPropertiesFunction.java | 115 ++
.../camel/test/CamelRouteCoverageDumper.java | 200 ++++
.../test/ExcludingPackageScanClassResolver.java | 33 +
.../camel/test/TestSupportNodeIdFactory.java | 30 +
.../apache/camel/test/junit5/CamelTestSupport.java | 1028 ++++++++++++++++++
.../test/junit5/EndpointUriFactoryTestSupport.java | 47 +
.../camel/test/junit5/ExchangeTestSupport.java | 57 +
.../camel/test/junit5/LanguageTestSupport.java | 100 ++
.../org/apache/camel/test/junit5/TestSupport.java | 539 +++++++++
.../apache/camel/test/junit5/params/Parameter.java | 25 +
.../camel/test/junit5/params/Parameterized.java | 30 +
.../test/junit5/params/ParameterizedExtension.java | 150 +++
.../camel/test/junit5/params/Parameters.java | 28 +
.../org/apache/camel/test/junit5/params/Test.java | 27 +
.../apache/camel/test/AvailablePortFinderTest.java | 60 ++
.../camel/test/junit5/CamelTestSupportTest.java | 75 ++
.../test/junit5/RouteFilterPatternExcludeTest.java | 56 +
.../RouteFilterPatternIncludeExcludeTest.java | 63 ++
.../test/junit5/RouteFilterPatternIncludeTest.java | 56 +
.../test/junit5/patterns/AdviceWithLambdaTest.java | 56 +
.../junit5/patterns/AdviceWithNotStartedTest.java | 74 ++
.../test/junit5/patterns/AsyncSendMockTest.java | 51 +
.../CreateCamelContextPerTestFalseTest.java | 116 ++
.../CreateCamelContextPerTestTrueTest.java | 148 +++
.../test/junit5/patterns/DebugJUnit5Test.java | 89 ++
.../patterns/DebugNoLazyTypeConverterTest.java | 89 ++
.../camel/test/junit5/patterns/DebugTest.java | 89 ++
.../FilterCreateCamelContextPerClassTest.java | 59 +
.../junit5/patterns/FilterFluentTemplateTest.java | 75 ++
.../test/junit5/patterns/FilterJUnit5Test.java | 69 ++
.../camel/test/junit5/patterns/FilterTest.java | 75 ++
.../test/junit5/patterns/GetMockEndpointTest.java | 45 +
.../patterns/IsMockEndpointsAndSkipJUnit5Test.java | 68 ++
.../junit5/patterns/IsMockEndpointsFileTest.java | 71 ++
.../junit5/patterns/IsMockEndpointsJUnit5Test.java | 74 ++
.../test/junit5/patterns/IsMockEndpointsTest.java | 65 ++
.../patterns/MockEndpointFailNoHeaderTest.java | 63 ++
.../camel/test/junit5/patterns/MyProduceBean.java | 32 +
.../camel/test/junit5/patterns/MySender.java | 25 +
.../test/junit5/patterns/ProduceBeanTest.java | 46 +
.../RouteBuilderConfigureExceptionTest.java | 54 +
.../RouteProcessorDumpRouteCoverageTest.java | 70 ++
.../junit5/patterns/SimpleMockEndpointsTest.java | 50 +
.../camel/test/junit5/patterns/SimpleMockTest.java | 45 +
.../junit5/patterns/SimpleNotifyBuilderTest.java | 53 +
.../patterns/SimpleWeaveAddMockLastTest.java | 59 +
...rridePropertiesWithPropertiesComponentTest.java | 76 ++
.../src/test/resources/log4j2.properties | 30 +
.../camel-test/camel-test-spring-junit5/pom.xml | 74 ++
.../services/org/apache/camel/other.properties | 7 +
.../generated/resources/test-spring-junit5.json | 15 +
.../src/main/docs/test-spring-junit5.adoc | 148 +++
.../spring/junit5/CamelAnnotationsHandler.java | 409 +++++++
.../junit5/CamelSpringBootExecutionListener.java | 125 +++
.../test/spring/junit5/CamelSpringBootTest.java | 42 +
.../camel/test/spring/junit5/CamelSpringTest.java | 43 +
.../junit5/CamelSpringTestContextLoader.java | 241 +++++
...ringTestContextLoaderTestExecutionListener.java | 46 +
.../test/spring/junit5/CamelSpringTestHelper.java | 116 ++
.../test/spring/junit5/CamelSpringTestSupport.java | 221 ++++
.../junit5/CamelTestContextBootstrapper.java | 31 +
.../camel/test/spring/junit5/DisableJmx.java | 43 +
.../test/spring/junit5/EnableRouteCoverage.java | 41 +
.../camel/test/spring/junit5/ExcludeRoutes.java | 44 +
.../camel/test/spring/junit5/MockEndpoints.java | 43 +
.../test/spring/junit5/MockEndpointsAndSkip.java | 43 +
.../test/spring/junit5/ProvidesBreakpoint.java | 36 +
.../test/spring/junit5/RouteCoverageDumper.java | 85 ++
.../spring/junit5/RouteCoverageEventNotifier.java | 50 +
.../camel/test/spring/junit5/ShutdownTimeout.java | 47 +
.../junit5/SpringTestExecutionListenerSorter.java | 45 +
.../junit5/StopWatchTestExecutionListener.java | 73 ++
.../camel/test/spring/junit5/UseAdviceWith.java | 46 +
...eOverridePropertiesWithPropertiesComponent.java | 35 +
...nterceptSendToEndpointWithLoadbalancerTest.java | 64 ++
.../test/issues/AdviceWithOnCompletionTest.java | 55 +
.../AdviceWithOnExceptionMultipleIssueTest.java | 98 ++
.../test/issues/MockEndpointsAndSkipTest.java | 45 +
.../test/patterns/DebugSpringCamelContextTest.java | 39 +
.../camel/test/patterns/DebugSpringTest.java | 86 ++
.../apache/camel/test/patterns/MyProduceBean.java | 38 +
.../org/apache/camel/test/patterns/MySender.java | 22 +
.../camel/test/patterns/ProduceBeanTest.java | 39 +
.../test/patterns/ProducerBeanInjectTest.java | 40 +
.../apache/camel/test/patterns/SimpleMockTest.java | 63 ++
.../test/spring/CamelSpringActiveProfileTest.java | 57 +
...CamelSpringDisableJmxInheritedOverrideTest.java | 34 +
.../spring/CamelSpringDisableJmxInheritedTest.java | 21 +
.../test/spring/CamelSpringDisableJmxTest.java | 34 +
.../test/spring/CamelSpringExcludeRoutesTest.java | 33 +
...ingOverridePropertiesForPropertyInjectTest.java | 56 +
.../spring/CamelSpringOverridePropertiesTest.java | 71 ++
.../camel/test/spring/CamelSpringPlainTest.java | 110 ++
.../CamelSpringPropertiesLocationElementTest.java | 65 ++
...amelSpringProvidesBreakpointInherritedTest.java | 22 +
.../spring/CamelSpringProvidesBreakpointTest.java | 67 ++
...lSpringRouteProcessorDumpRouteCoverageTest.java | 56 +
...SpringShutdownTimeoutInheritedOverrideTest.java | 36 +
.../CamelSpringShutdownTimeoutInheritedTest.java | 22 +
.../spring/CamelSpringShutdownTimeoutTest.java | 36 +
.../spring/CamelSpringTestPropertySourceTest.java | 47 +
.../CamelSpringTestSupportActiveProfileTest.java | 53 +
.../test/spring/CamelSpringUseAdviceWithTest.java | 52 +
.../SpringTestExecutionListenerSorterTest.java | 50 +
.../spring/TestPropertyInjectRouteBuilder.java | 33 +
.../apache/camel/test/spring/TestRouteBuilder.java | 29 +
.../src/test/resources/jndi.properties | 22 +
.../src/test/resources/log4j2.properties | 30 +
...InterceptSendToEndpointWithLoadbalancerTest.xml | 34 +
.../test/issues/AdviceWithOnCompletionTest.xml | 40 +
.../AdviceWithOnExceptionMultipleIssueTest.xml | 50 +
.../camel/test/issues/MockEndpointsAndSkipTest.xml | 38 +
.../camel/test/patterns/ProduceBeanInjectTest.xml | 44 +
.../apache/camel/test/patterns/ProduceBeanTest.xml | 36 +
.../apache/camel/test/patterns/SimpleMockTest.xml | 34 +
.../camel/test/patterns/applicationContext.xml | 42 +
.../CamelSpringActiveProfileTest-context.xml | 46 +
...ridePropertiesForPropertyInjectTest-context.xml | 40 +
.../CamelSpringOverridePropertiesTest-context.xml | 45 +
.../test/spring/CamelSpringPlainTest-context.xml | 48 +
...SpringPropertiesLocationElementTest-context.xml | 56 +
.../CamelSpringTestPropertySourceTest-context.xml | 44 +
.../properties-location-element-1.properties | 18 +
.../properties-location-element-2.properties | 18 +
.../properties-location-element-3.properties | 18 +
.../org/apache/camel/test/spring/test.properties | 19 +
components/camel-test/camel-test-spring/pom.xml | 74 ++
.../services/org/apache/camel/other.properties | 7 +
.../src/generated/resources/test-spring.json | 15 +
.../src/main/docs/test-spring.adoc | 417 +++++++
.../camel/test/spring/CamelAnnotationsHandler.java | 409 +++++++
.../spring/CamelSpringBootJUnit4ClassRunner.java | 33 +
.../camel/test/spring/CamelSpringBootRunner.java | 32 +
.../CamelSpringDelegatingTestContextLoader.java | 118 ++
.../test/spring/CamelSpringJUnit4ClassRunner.java | 33 +
.../camel/test/spring/CamelSpringRunner.java | 93 ++
.../test/spring/CamelSpringTestContextLoader.java | 241 +++++
...ringTestContextLoaderTestExecutionListener.java | 48 +
.../camel/test/spring/CamelSpringTestHelper.java | 116 ++
.../camel/test/spring/CamelSpringTestSupport.java | 213 ++++
.../test/spring/CamelTestContextBootstrapper.java | 31 +
.../org/apache/camel/test/spring/DisableJmx.java | 43 +
.../camel/test/spring/EnableRouteCoverage.java | 41 +
.../apache/camel/test/spring/ExcludeRoutes.java | 44 +
.../apache/camel/test/spring/MockEndpoints.java | 43 +
.../camel/test/spring/MockEndpointsAndSkip.java | 43 +
.../camel/test/spring/ProvidesBreakpoint.java | 36 +
.../camel/test/spring/RouteCoverageDumper.java | 85 ++
.../test/spring/RouteCoverageEventNotifier.java | 50 +
.../apache/camel/test/spring/ShutdownTimeout.java | 47 +
.../spring/StopWatchTestExecutionListener.java | 63 ++
.../apache/camel/test/spring/UseAdviceWith.java | 46 +
...eOverridePropertiesWithPropertiesComponent.java | 35 +
...nterceptSendToEndpointWithLoadbalancerTest.java | 69 ++
.../test/issues/AdviceWithOnCompletionTest.java | 61 ++
.../AdviceWithOnExceptionMultipleIssueTest.java | 117 ++
.../test/issues/MockEndpointsAndSkipTest.java | 45 +
.../test/patterns/DebugSpringCamelContextTest.java | 39 +
.../camel/test/patterns/DebugSpringTest.java | 80 ++
.../apache/camel/test/patterns/MyProduceBean.java | 41 +
.../org/apache/camel/test/patterns/MySender.java | 25 +
.../camel/test/patterns/ProduceBeanTest.java | 42 +
.../test/patterns/ProducerBeanInjectTest.java | 38 +
.../apache/camel/test/patterns/SimpleMockTest.java | 63 ++
.../test/spring/CamelSpringActiveProfileTest.java | 57 +
...ingOverridePropertiesForPropertyInjectTest.java | 57 +
.../spring/CamelSpringOverridePropertiesTest.java | 72 ++
.../CamelSpringPropertiesLocationElementTest.java | 67 ++
...lSpringRouteProcessorDumpRouteCoverageTest.java | 55 +
...pringRunnerDisableJmxInheritedOverrideTest.java | 33 +
.../CamelSpringRunnerDisableJmxInheritedTest.java | 21 +
.../spring/CamelSpringRunnerDisableJmxTest.java | 33 +
.../spring/CamelSpringRunnerExcludeRoutesTest.java | 32 +
.../test/spring/CamelSpringRunnerPlainTest.java | 112 ++
...ringRunnerProvidesBreakpointInherritedTest.java | 22 +
.../CamelSpringRunnerProvidesBreakpointTest.java | 66 ++
...RunnerShutdownTimeoutInheritedOverrideTest.java | 35 +
...elSpringRunnerShutdownTimeoutInheritedTest.java | 22 +
.../CamelSpringRunnerShutdownTimeoutTest.java | 35 +
.../CamelSpringRunnerTestPropertySourceTest.java | 49 +
.../spring/CamelSpringRunnerUseAdviceWithTest.java | 50 +
.../CamelSpringTestSupportActiveProfileTest.java | 52 +
.../spring/TestPropertyInjectRouteBuilder.java | 33 +
.../apache/camel/test/spring/TestRouteBuilder.java | 30 +
.../src/test/resources/jndi.properties | 22 +
.../src/test/resources/log4j2.properties | 30 +
...InterceptSendToEndpointWithLoadbalancerTest.xml | 32 +
.../test/issues/AdviceWithOnCompletionTest.xml | 39 +
.../AdviceWithOnExceptionMultipleIssueTest.xml | 49 +
.../camel/test/issues/MockEndpointsAndSkipTest.xml | 37 +
.../camel/test/patterns/ProduceBeanInjectTest.xml | 41 +
.../apache/camel/test/patterns/ProduceBeanTest.xml | 35 +
.../apache/camel/test/patterns/SimpleMockTest.xml | 33 +
.../camel/test/patterns/applicationContext.xml | 40 +
.../CamelSpringActiveProfileTest-context.xml | 43 +
...ridePropertiesForPropertyInjectTest-context.xml | 36 +
.../CamelSpringOverridePropertiesTest-context.xml | 42 +
...SpringPropertiesLocationElementTest-context.xml | 49 +
.../spring/CamelSpringRunnerPlainTest-context.xml | 44 +
...lSpringRunnerTestPropertySourceTest-context.xml | 41 +
.../properties-location-element-1.properties | 18 +
.../properties-location-element-2.properties | 18 +
.../properties-location-element-3.properties | 18 +
.../org/apache/camel/test/spring/test.properties | 19 +
components/camel-test/camel-test/pom.xml | 167 +++
.../services/org/apache/camel/other.properties | 7 +
.../camel-test/src/generated/resources/test.json | 15 +
.../camel-test/camel-test/src/main/docs/test.adoc | 54 +
.../org/apache/camel/test/AvailablePortFinder.java | 93 ++
.../AvailablePortFinderPropertiesFunction.java | 115 ++
.../test/ExcludingPackageScanClassResolver.java | 33 +
.../camel/test/TestSupportNodeIdFactory.java | 30 +
.../camel/test/junit4/CamelTearDownRule.java | 49 +
.../apache/camel/test/junit4/CamelTestSupport.java | 1140 ++++++++++++++++++++
.../apache/camel/test/junit4/CamelTestWatcher.java | 44 +
.../camel/test/junit4/ExchangeTestSupport.java | 55 +
.../camel/test/junit4/LanguageTestSupport.java | 97 ++
.../org/apache/camel/test/junit4/TestSupport.java | 564 ++++++++++
.../apache/camel/test/AvailablePortFinderTest.java | 106 ++
.../apache/camel/test/CamelTestSupportTest.java | 71 ++
.../camel/test/RouteFilterPatternExcludeTest.java | 57 +
.../test/RouteFilterPatternIncludeExcludeTest.java | 65 ++
.../camel/test/RouteFilterPatternIncludeTest.java | 57 +
.../camel/test/patterns/AdviceWithLambdaTest.java | 57 +
.../test/patterns/AdviceWithNotStartedTest.java | 72 ++
.../camel/test/patterns/AsyncSendMockTest.java | 49 +
.../CreateCamelContextPerTestFalseTest.java | 116 ++
.../CreateCamelContextPerTestTrueTest.java | 148 +++
.../camel/test/patterns/DebugJUnit4Test.java | 87 ++
.../patterns/DebugNoLazyTypeConverterTest.java | 92 ++
.../org/apache/camel/test/patterns/DebugTest.java | 87 ++
.../FilterCreateCamelContextPerClassTest.java | 65 ++
.../test/patterns/FilterFluentTemplateTest.java | 75 ++
.../camel/test/patterns/FilterJUnit4Test.java | 69 ++
.../org/apache/camel/test/patterns/FilterTest.java | 75 ++
.../camel/test/patterns/GetMockEndpointTest.java | 46 +
.../patterns/IsMockEndpointsAndSkipJUnit4Test.java | 63 ++
.../test/patterns/IsMockEndpointsFileTest.java | 69 ++
.../test/patterns/IsMockEndpointsJUnit4Test.java | 70 ++
.../camel/test/patterns/IsMockEndpointsTest.java | 63 ++
.../patterns/MockEndpointFailNoHeaderTest.java | 63 ++
.../apache/camel/test/patterns/MyProduceBean.java | 32 +
.../org/apache/camel/test/patterns/MySender.java | 25 +
.../camel/test/patterns/ProduceBeanTest.java | 47 +
.../RouteBuilderConfigureExceptionTest.java | 54 +
.../RouteProcessorDumpRouteCoverageTest.java | 58 +
.../test/patterns/SimpleMockEndpointsTest.java | 52 +
.../apache/camel/test/patterns/SimpleMockTest.java | 46 +
.../test/patterns/SimpleNotifyBuilderTest.java | 57 +
.../test/patterns/SimpleWeaveAddMockLastTest.java | 62 ++
...rridePropertiesWithPropertiesComponentTest.java | 78 ++
.../camel-test/src/test/resources/jndi.properties | 22 +
.../src/test/resources/log4j2.properties | 30 +
.../camel-test/camel-testcontainers-junit5/pom.xml | 147 +++
.../services/org/apache/camel/other.properties | 7 +
.../generated/resources/testcontainers-junit5.json | 15 +
.../src/main/docs/testcontainers-junit5.adoc | 143 +++
.../junit5/ContainerAwareTestSupport.java | 113 ++
.../junit5/ContainerPropertiesFunction.java | 79 ++
.../test/testcontainers/junit5/Containers.java | 90 ++
.../camel/test/testcontainers/junit5/Wait.java | 73 ++
.../junit5/ContainerAwareTestSupportIT.java | 53 +
.../src/test/resources/log4j2.properties | 30 +
.../camel-testcontainers-spring-junit5/pom.xml | 147 +++
.../services/org/apache/camel/other.properties | 7 +
.../resources/testcontainers-spring-junit5.json | 15 +
.../main/docs/testcontainers-spring-junit5.adoc | 35 +
.../junit5/ContainerAwareSpringTestSupport.java | 116 ++
.../junit5/ContainerAwareSpringTestSupportIT.java | 62 ++
.../src/test/resources/log4j2.properties | 30 +
.../junit5/ContainerAwareSpringTestSupportTest.xml | 33 +
.../camel-test/camel-testcontainers-spring/pom.xml | 147 +++
.../services/org/apache/camel/other.properties | 7 +
.../generated/resources/testcontainers-spring.json | 15 +
.../src/main/docs/testcontainers-spring.adoc | 15 +
.../spring/ContainerAwareSpringTestSupport.java | 117 ++
.../spring/ContainerAwareSpringTestSupportIT.java | 62 ++
.../src/test/resources/log4j2.properties | 30 +
.../spring/ContainerAwareSpringTestSupportTest.xml | 33 +
components/camel-test/camel-testcontainers/pom.xml | 147 +++
.../services/org/apache/camel/other.properties | 7 +
.../src/generated/resources/testcontainers.json | 15 +
.../src/main/docs/testcontainers.adoc | 123 +++
.../testcontainers/ContainerAwareTestSupport.java | 115 ++
.../ContainerPropertiesFunction.java | 80 ++
.../camel/test/testcontainers/Containers.java | 90 ++
.../org/apache/camel/test/testcontainers/Wait.java | 77 ++
.../ContainerAwareTestSupportIT.java | 53 +
.../src/test/resources/log4j2.properties | 30 +
315 files changed, 23185 insertions(+)
diff --git a/components/camel-test/camel-test-cdi/pom.xml b/components/camel-test/camel-test-cdi/pom.xml
new file mode 100644
index 0000000..fe65588
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test-parent</artifactId>
+ <version>3.9.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-test-cdi</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Camel :: Test :: CDI</name>
+ <description>Camel unit testing with CDI</description>
+
+ <properties>
+ <firstVersion>2.17.0</firstVersion>
+ <label>testing,java</label>
+ <title>Test CDI</title>
+
+ </properties>
+
+ <dependencies>
+
+ <!-- compile dependencies -->
+
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-cdi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.weld.se</groupId>
+ <artifactId>weld-se-core</artifactId>
+ <version>${weld3-version}</version>
+ </dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-slf4j-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+</project>
diff --git a/components/camel-test/camel-test-cdi/src/generated/resources/META-INF/services/org/apache/camel/other.properties b/components/camel-test/camel-test-cdi/src/generated/resources/META-INF/services/org/apache/camel/other.properties
new file mode 100644
index 0000000..7995122
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/generated/resources/META-INF/services/org/apache/camel/other.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+name=test-cdi
+groupId=org.apache.camel
+artifactId=camel-test-cdi
+version=3.9.0-SNAPSHOT
+projectName=Camel :: Test :: CDI
+projectDescription=Camel unit testing with CDI
diff --git a/components/camel-test/camel-test-cdi/src/generated/resources/test-cdi.json b/components/camel-test/camel-test-cdi/src/generated/resources/test-cdi.json
new file mode 100644
index 0000000..9274c90
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/generated/resources/test-cdi.json
@@ -0,0 +1,15 @@
+{
+ "other": {
+ "kind": "other",
+ "name": "test-cdi",
+ "title": "Test CDI",
+ "description": "Camel unit testing with CDI",
+ "deprecated": false,
+ "firstVersion": "2.17.0",
+ "label": "testing,java",
+ "supportLevel": "Stable",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-test-cdi",
+ "version": "3.9.0-SNAPSHOT"
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/docs/test-cdi.adoc b/components/camel-test/camel-test-cdi/src/main/docs/test-cdi.adoc
new file mode 100644
index 0000000..03f617e
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/docs/test-cdi.adoc
@@ -0,0 +1,738 @@
+[[test-cdi-other]]
+= Test CDI Component
+:docTitle: Test CDI
+:artifactId: camel-test-cdi
+:description: Camel unit testing with CDI
+:since: 2.17
+:supportLevel: Stable
+include::{cq-version}@camel-quarkus:ROOT:partial$reference/others/test-cdi.adoc[opts=optional]
+
+*Since Camel {since}*
+
+http://camel.apache.org/testing.html[Testing] is a crucial part of any
+development or integration work. In case you're using the Camel CDI
+integration for your applications, you have a number of options to ease
+testing.
+
+You can use CDI for IoC and the Camel testing endpoints like
+`DataSet`, `Mock`, `Test` and testing API like `AdviceWith`
+and `NotifyBuilder` to create sophisticated integration/unit tests that
+are easy to run and debug inside your IDE.
+
+There are a number of supported approaches for testing with CDI in
+Camel:
+
+[width="100%",cols="1,1,4",options="header",]
+|=======================================================================
+|Name |Testing Frameworks Supported |Description
+|<<CDITesting-CamelCDITest>>[Camel CDI Test] a|
+* JUnit 4
+
+ a|
+The Camel CDI test module (`camel-test-cdi`) provides a JUnit runner
+that bootstraps a test environment using CDI so that you don't have to
+be familiar with any CDI testing frameworks and can concentrate on the
+testing logic of your Camel CDI applications.
+
+|<<CDITesting-Arquillian>>[Arquillian] a|
+* JUnit 4
+* TestNG 5
+
+ |http://arquillian.org/[Arquillian] is a testing platform that handles
+all the plumbing of in-container testing with support for a wide range
+of http://arquillian.org/modules/[target containers]. Arquillian can be
+configured to run your test classes in _embedded_ (in JVM CDI),
+_managed_ (a real Web server or Java EE application server instance
+started in a separate process) or _remote_ (the lifecycle of the
+container isn't managed by Arquillian) modes. You have to create the
+System Under Test (SUT) in your test classes using
+http://arquillian.org/guides/shrinkwrap_introduction/[ShrinkWrap
+descriptors]. The benefit is that you have a very fine-grained control
+over the application configuration that you want to test. The downside
+is more code and more complex _classpath_ / class loading structure.
+
+|<<CDITesting-PAXExam>>[PAX Exam] a|
+* JUnit 4
+* TestNG 6
+
+ |https://ops4j1.jira.com/wiki/display/PAXEXAM4[PAX Exam] lets you test
+your Camel applications in OSGi, Java EE or standalone CDI containers
+with the ability to finely configure your System Under Test (SUT),
+similarly to Arquillian. You can use it to test your Camel CDI
+applications that target OSGi environments like Karaf with
+https://ops4j1.jira.com/wiki/display/PAXCDI/Pax+CDI[PAX CDI], but you
+can use it as well to test your Camel CDI applications in standalone
+https://ops4j1.jira.com/wiki/display/PAXEXAM4/CDI+Containers[CDI
+containers],
+https://ops4j1.jira.com/wiki/display/PAXEXAM4/Web+Containers[Web
+containers] and
+https://ops4j1.jira.com/wiki/display/PAXEXAM4/Java+EE+Containers[Java EE
+containers].
+|=======================================================================
+
+[[CDITesting-CamelCDITest]]
+== Camel CDI Test
+
+With this approach, your test classes use the JUnit runner provided in
+Camel CDI test. This runner manages the lifecycle of a standalone CDI
+container and automatically assemble and deploy the System Under Test
+(SUT) based on the _classpath_ into the container.
+
+It deploys the test class as a CDI bean so that dependency injection and
+any CDI features is available within the test class.
+
+Maven users will need to add the following dependency to
+their `pom.xml` for this component:
+
+[source,xml]
+----
+<dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test-cdi</artifactId>
+ <scope>test</scope>
+ <version>x.x.x</version>
+ <!-- use the same version as your Camel core version -->
+</dependency>
+----
+
+Here is a simple unit test using the `CamelCdiRunner`:
+
+[source,java]
+----
+@RunWith(CamelCdiRunner.class)
+public class CamelCdiTest {
+
+ @Inject
+ CamelContext context;
+
+ @Test
+ public void test() {
+ assertThat("Camel context status is incorrect!",
+ context.getStatus(),
+ is(equalTo(ServiceStatus.Started)));
+ }
+}
+----
+
+CDI injection is also available for test method parameters, e.g.:
+
+[source,java]
+----
+@RunWith(CamelCdiRunner.class)
+public class CamelCdiTest {
+
+ @Test
+ public void test(@Uri("direct:foo") ProducerTemplate producer) {
+ producer.sendBody("bar");
+ }
+}
+----
+
+Camel CDI test provides the `@Order` annotation that you can use to
+execute the test methods in a particular sequence, e.g.:
+
+[source,java]
+----
+@RunWith(CamelCdiRunner.class)
+public class CamelCdiTest {
+
+ @Test
+ @Order(1)
+ public void firstTestMethod() {
+ }
+
+ @Test
+ @Order(2)
+ public void secondTestMethod() {
+ }
+}
+----
+
+One CDI container is bootstrapped for the entire execution of the test
+class.
+
+Besides, the test class is deployed as a CDI bean, so that you can
+control how the runner instantiate the test class, either one test class
+instance for each test method (the default, depending on the _built-in_
+default `@Dependent` CDI scope), or one test class instance for the
+entire test class execution using the `@ApplicationScoped` scope, e.g.:
+
+[source,java]
+----
+@ApplicationScoped
+@RunWith(CamelCdiRunner.class)
+public class CamelCdiTest {
+
+ int counter;
+
+ @Test
+ @Order(1)
+ public void firstTestMethod() {
+ counter++;
+ }
+
+ @Test
+ @Order(2)
+ public void secondTestMethod() {
+ assertEquals(counter, 1);
+ }
+}
+----
+
+In case you need to add additional test beans, you can use the `@Beans`
+annotation provided by Camel CDI test. For example, if you need to add
+a route to your Camel context, instead of declaring a `RouteBuilder` bean
+with a nested class, you can declare a managed bean, e.g.:
+
+[source,java]
+----
+class TestRoute extends RouteBuilder {
+
+ @Override
+ public void configure() {
+ from("direct:foo").to("mock:bar");
+ }
+}
+----
+
+And add it with the `@Beans` annotation, e.g.:
+
+[source,java]
+----
+@RunWith(CamelCdiRunner.class)
+@Beans(classes = TestRoute.class)
+public class CamelCdiTest {
+
+}
+----
+
+[[CDITesting-Arquillian]]
+== Arquillian
+
+With this approach, you use the JUnit runner or TestNG support provided
+by Arquillian to delegate the bootstrap of the CDI container. You need
+to declare a `@Deployment` method to create your application
+configuration to be deployed in the container using
+http://arquillian.org/guides/shrinkwrap_introduction/[ShrinkWrap
+descriptors], e.g.:
+
+[source,java]
+----
+@RunWith(Arquillian.class)
+public class CamelCdiJavaSeTest {
+
+ @Deployment
+ public static Archive deployment() {
+ return ShrinkWrap.create(JavaArchive.class)
+ // Camel CDI
+ .addPackage(CdiCamelExtension.class.getPackage())
+ // Test classes
+ .addPackage(Application.class.getPackage())
+ // Bean archive deployment descriptor
+ .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
+ }
+
+ @Inject
+ CamelContext context;
+
+ @Test
+ public void test() {
+ assertThat("Camel context status is incorrect!",
+ context.getStatus(),
+ is(equalTo(ServiceStatus.Started)));
+ }
+}
+----
+
+In that example, you can use any Java SE Arquillian embedded container
+adapter, like the
+http://arquillian.org/modules/arquillian-weld-se-embedded-1.1-container-adapter/[Weld
+embedded container adapter] e.g. with Maven you need that complete set
+of dependencies:
+
+[source,xml]
+----
+<dependencies>
+
+ <dependency>
+ <groupId>org.jboss.arquillian.junit</groupId>
+ <artifactId>arquillian-junit-container</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.shrinkwrap.descriptors</groupId>
+ <artifactId>shrinkwrap-descriptors-depchain</artifactId>
+ <type>pom</type>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.arquillian.container</groupId>
+ <artifactId>arquillian-weld-se-embedded-1.1</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.weld</groupId>
+ <artifactId>weld-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+</dependencies>
+----
+
+Using ShrinkWarp Descriptors, you have a complete control over the
+configuration and kind of Camel CDI applications you want to test. For
+example, to test a Camel CDI application that uses the Camel
+REST DSL configured with the
+xref:ROOT:servlet-component.adoc[Servlet component], you need to create a Web archive,
+e.g.:
+
+[source,java]
+----
+@RunWith(Arquillian.class)
+public class CamelCdiWebTest {
+
+ @Deployment
+ public static Archive<?> createTestArchive() {
+ return ShrinkWrap.create(WebArchive.class)
+ .addClass(Application.class)
+ .addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"))
+ .setWebXML(Paths.get("src/main/webapp/WEB-INF/web.xml").toFile());
+ }
+
+ @Test
+ @RunAsClient
+ public void test(@ArquillianResource URL url) throws Exception {
+ assertThat(IOHelper.loadText(new URL(url, "camel/rest/hello").openStream()),
+ is(equalTo("Hello World!\n")));
+ }
+}
+----
+
+In the example above, you can use any Arquillian Web container adapter,
+like
+the http://arquillian.org/modules/arquillian-jetty-embedded-9-container-adapter/[Jetty
+embedded container adapter] e.g. with Maven you need the
+complete following set of dependencies:
+
+[source,xml]
+----
+</dependencies>
+
+ <dependency>
+ <groupId>org.jboss.arquillian.junit</groupId>
+ <artifactId>arquillian-junit-container</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.arquillian.testenricher</groupId>
+ <artifactId>arquillian-testenricher-resource</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.shrinkwrap.descriptors</groupId>
+ <artifactId>shrinkwrap-descriptors-depchain</artifactId>
+ <type>pom</type>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.weld.servlet</groupId>
+ <artifactId>weld-servlet</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-webapp</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-annotations</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.arquillian.container</groupId>
+ <artifactId>arquillian-jetty-embedded-9</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+</dependencies>
+----
+
+You can see the tests in the `camel-example-cdi-rest-servlet` example
+for a complete working example of testing a Camel CDI application using
+the REST DSL and deployed as a WAR in Jetty.
+
+[[CDITesting-PAXExam]]
+== PAX Exam
+
+If you target OSGi as runtime environment for your Camel CDI
+applications, you can use PAX Exam to automate the deployment of your
+tests into an OSGi container, for example into Karaf, e.g.:
+
+[source,java]
+----
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class PaxCdiOsgiTest {
+
+ @Configuration
+ public Option[] config() throws IOException {
+ return options(
+ // Karaf configuration
+ karafDistributionConfiguration()
+ .frameworkUrl(
+ maven()
+ .groupId("org.apache.karaf")
+ .artifactId("apache-karaf")
+ .versionAsInProject()
+ .type("zip"))
+ .name("Apache Karaf")
+ .unpackDirectory(new File("target/paxexam/unpack/")),
+ // PAX CDI Weld
+ features(
+ maven()
+ .groupId("org.ops4j.pax.cdi")
+ .artifactId("pax-cdi-features")
+ .type("xml")
+ .classifier("features")
+ .versionAsInProject(),
+ "pax-cdi-weld"),
+ // Karaf Camel commands
+ mavenBundle()
+ .groupId("your.application.groupId")
+ .artifactId("your.application.artifactId")
+ .versionAsInProject()
+ );
+ }
+
+ @Inject
+ private CamelContext context;
+
+ @Test
+ public void testContextStatus() {
+ assertThat("Camel context status is incorrect!",
+ context.getStatus(), equalTo(ServiceStatus.Started));
+ }
+}
+----
+
+You can see the tests in the `camel-example-cdi-osgi` example for a
+complete working example of testing a Camel CDI application deployed in
+an OSGi container using PAX Exam.
+
+[[CDITesting-TestingPatterns]]
+== Testing Patterns
+
+You can see the tests in the `camel-example-cdi-test` example for a
+thorough overview of the following testing patterns for Camel CDI
+applications.
+
+[NOTE]
+====
+While the patterns above are illustrated using the Camel CDI test
+module, they should equally work with Arquillian and PAX Exam unless
+otherwise stated or illustrated with a specific example.
+====
+
+[[CDITesting-Testroutes]]
+== Test routes
+
+You may want to add some Camel routes to your Camel CDI applications for
+testing purpose. For example to route some exchanges to a `MockEndpoint`
+instance. You can do that by declaring a `RouteBuilder` bean within the
+test class as you would normally do in your application code, e.g.:
+
+[source,java]
+----
+@RunWith(CamelCdiRunner.class)
+public class CamelCdiTest {
+
+ // Declare a RouteBuilder bean for testing purpose
+ // that is automatically added to the Camel context
+ static class TestRoute extends RouteBuilder {
+
+ @Override
+ public void configure() {
+ from("direct:out").routeId("test").to("mock:out");
+ }
+
+ }
+
+ // And retrieve the MockEndpoint for further assertions
+ @Inject
+ @Uri("mock:out")
+ MockEndpoint mock;
+}
+----
+
+You can find more information in xref:cdi.adoc#CDI-Auto-detectingCamelroutes[auto-detecting Camel
+routes].
+
+In case you prefer declaring the `RouteBuilder` bean in a separate class,
+for example to share it more easily across multiple test classes, you can use
+the `@Beans` annotation to instruct Camel CDI test to deploy that class as a
+CDI bean, e.g.:
+
+[source,java]
+----
+@RunWith(CamelCdiRunner.class)
+@Beans(classes = TestRoute.class)
+public class CamelCdiTest {
+
+ // ...
+}
+----
+
+[[CDITesting-Beanalternatives]]
+== Bean alternatives
+
+You may want to replace a bean that is used in your Camel routes by
+another bean for testing purpose, for example to mock it or change the
+behavior of the application bean.
+
+Imagine you have the following route in your application:
+
+[source,java]
+----
+public class Application {
+
+ @ApplicationScoped
+ static class Hello extends RouteBuilder {
+
+ @Override
+ public void configure() {
+ from("direct:in").bean("bean").to("direct:out");
+ }
+ }
+}
+----
+
+And the corresponding bean:
+
+[source,java]
+----
+@Named("bean")
+public class Bean {
+
+ public String process(@Body String body) {
+ return body;
+ }
+}
+----
+
+Then you can replace the bean above in your tests by declaring an
+_alternative_ bean, annotated with `@Alternative`, e.g.:
+
+[source,java]
+----
+@Alternative
+@Named("bean")
+public class AlternativeBean {
+
+ public String process(@Body String body) {
+ return body + " with alternative bean!";
+ }
+}
+----
+
+And you need to activate (a.k.a. _select_ in CDI terminology) this
+alternative bean in your tests. If your using the `CamelCdiRunner` JUnit
+runner, you can do that with the `@Beans` annotation provided by the
+Camel CDI test module, e.g.:
+
+[source,java]
+----
+@RunWith(CamelCdiRunner.class)
+@Beans(alternatives = AlternativeBean.class)
+public class CamelCdiTest {
+
+ @Test
+ public void testAlternativeBean(@Uri("direct:in") ProducerTemplate producer
+ @Uri("mock:out") MockEndpoint mock) throws InterruptedException {
+ mock.expectedMessageCount(1);
+ mock.expectedBodiesReceived("test with alternative bean!");
+
+ producer.sendBody("test");
+
+ MockEndpoint.assertIsSatisfied(1L, TimeUnit.SECONDS, mock);
+ }
+
+ static class TestRoute extends RouteBuilder {
+
+ @Override
+ public void configure() {
+ from("direct:out").routeId("test").to("mock:out");
+ }
+ }
+}
+----
+
+If you're using Arquillian as testing framework, you need to activate the
+alternative in your deployment method, e.g.:
+
+[source,java]
+----
+@RunWith(Arquillian.class)
+public class CamelCdiTest {
+
+ @Deployment
+ public static Archive deployment() {
+ return ShrinkWrap.create(JavaArchive.class)
+ // Camel CDI
+ .addPackage(CdiCamelExtension.class.getPackage())
+ // Test classes
+ .addPackage(Application.class.getPackage())
+ // Bean archive deployment descriptor
+ .addAsManifestResource(
+ new StringAsset(
+ Descriptors.create(BeansDescriptor.class)
+ .getOrCreateAlternatives()
+ .stereotype(MockAlternative.class.getName()).up()
+ .exportAsString()),
+ "beans.xml");
+ }
+
+ //...
+}
+----
+
+[[CDITesting-Camelcontextcustomization]]
+== Camel context customization
+
+You may need to customize your Camel contexts for testing purpose, for
+example disabling JMX management to avoid TCP port allocation conflict.
+You can do that by declaring a custom Camel context bean in your test
+class, e.g.:
+
+[source,java]
+----
+@RunWith(CamelCdiRunner.class)
+public class CamelCdiTest {
+
+ @Default
+ @Named("camel-test-cdi")
+ @ApplicationScoped
+ static class CustomCamelContext extends DefaultCamelContext {
+
+ @PostConstruct
+ void customize() {
+ disableJMX();
+ }
+ }
+}
+----
+
+In that example, the custom Camel context bean declared in the test
+class will be used during the test execution instead of the default
+Camel context bean provided by the Camel CDI component.
+
+[[CDITesting-RoutesadvisingwithadviceWith]]
+== Routes advising with `adviceWith`
+
+`AdviceWith` is used for testing Camel routes where you
+can _advice_ an existing route before its being tested. It allows to
+add http://camel.apache.org/intercept.html[Intercept] or _weave_ routes
+for testing purpose, for example using
+the xref:ROOT:mock-component.adoc[Mock] component.
+
+It is recommended to only advice routes which are not started already.
+To meet that requirement, you can use the `CamelContextStartingEvent`
+event by declaring an observer method in which you use `adviceWith` to
+add a `mock` endpoint at the end of your Camel route, e.g.:
+
+[source,java]
+----
+@RunWith(CamelCdiRunner.class)
+public class CamelCdiTest {
+
+ void advice(@Observes CamelContextStartingEvent event,
+ @Uri("mock:test") MockEndpoint messages,
+ ModelCamelContext context) throws Exception {
+
+ context.getRouteDefinition("route")
+ .adviceWith(context, new AdviceWithRouteBuilder() {
+ @Override
+ public void configure() {
+ weaveAddLast().to("mock:test");
+ }
+ });
+ }
+}
+----
+
+[[CDITesting-JUnitrules]]
+== JUnit rules
+
+Camel CDI test starts the CDI container after all the JUnit class rules
+have executed.
+
+That way, you can use JUnit class rules to initialize (resp. clean-up)
+resources that your test classes would require during their execution
+before the container initializes (resp. after the container has
+shutdown). For example, you could use an embedded JMS broker
+like https://activemq.apache.org/artemis/[ActiveMQ Artemis] to test your
+Camel JMS application, e.g.:
+
+[source,java]
+----
+import org.apache.activemq.artemis.jms.server.embedded.EmbeddedJMS;
+
+@RunWith(CamelCdiRunner.class)
+public class CamelCdiTest {
+
+ @ClassRule
+ public static final ExternalResource resources = new ExternalResource() {
+
+ private final EmbeddedJMS jms = new EmbeddedJMS();
+
+ @Override
+ protected void before() throws Exception {
+ jms.start();
+ }
+
+ @Override
+ protected void after() throws Exception {
+ jms.stop();
+ }
+ };
+
+ @Inject
+ @Uri("jms:destination")
+ private ProducerTemplate producer;
+
+ @Test
+ public void sendMessage() {
+ producer.sendBody("message");
+ }
+}
+----
+
+Another use case is to assert the behavior of your application after it
+has shutdown. In that case, you can use the `Verifier` rule, e.g.:
+
+[source,java]
+----
+import org.junit.rules.Verifier;
+
+@RunWith(CamelCdiRunner.class)
+public class CamelCdiTest {
+
+ @ClassRule
+ public static Verifier verifier = new Verifier() {
+
+ @Override
+ protected void verify() {
+ // Executes after the CDI container has shutdown
+ }
+ };
+}
+----
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/AnnotatedDecorator.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/AnnotatedDecorator.java
new file mode 100644
index 0000000..60957aa
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/AnnotatedDecorator.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.cdi;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.enterprise.inject.spi.Annotated;
+
+class AnnotatedDecorator implements Annotated {
+
+ private final Annotated decorated;
+
+ private final Set<Annotation> annotations;
+
+ AnnotatedDecorator(Annotated decorated, Set<Annotation> annotations) {
+ this.decorated = decorated;
+ this.annotations = annotations;
+ }
+
+ @Override
+ public Type getBaseType() {
+ return decorated.getBaseType();
+ }
+
+ @Override
+ public Set<Type> getTypeClosure() {
+ return decorated.getTypeClosure();
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+ T annotation = getDecoratingAnnotation(annotationType);
+ if (annotation != null) {
+ return annotation;
+ } else {
+ return decorated.getAnnotation(annotationType);
+ }
+ }
+
+ @Override
+ public Set<Annotation> getAnnotations() {
+ Set<Annotation> annotations = new HashSet<>(this.annotations);
+ annotations.addAll(decorated.getAnnotations());
+ return Collections.unmodifiableSet(annotations);
+ }
+
+ @Override
+ public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+ return getDecoratingAnnotation(annotationType) != null || decorated.isAnnotationPresent(annotationType);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T extends Annotation> T getDecoratingAnnotation(Class<T> annotationType) {
+ for (Annotation annotation : annotations) {
+ if (annotationType.isAssignableFrom(annotation.annotationType())) {
+ return (T) annotation;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public <T extends Annotation> Set<T> getAnnotations(Class<T> annotationType) {
+ return decorated.getAnnotations(annotationType);
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/AnnotatedMethodDecorator.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/AnnotatedMethodDecorator.java
new file mode 100644
index 0000000..fb12dbc
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/AnnotatedMethodDecorator.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.cdi;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+
+import javax.enterprise.inject.spi.AnnotatedMethod;
+import javax.enterprise.inject.spi.AnnotatedParameter;
+import javax.enterprise.inject.spi.AnnotatedType;
+
+final class AnnotatedMethodDecorator<X> extends AnnotatedDecorator implements AnnotatedMethod<X> {
+
+ private final AnnotatedMethod<X> decoratedMethod;
+
+ AnnotatedMethodDecorator(AnnotatedMethod<X> decoratedMethod, Annotation decoratingAnnotation) {
+ super(decoratedMethod, Collections.singleton(decoratingAnnotation));
+ this.decoratedMethod = decoratedMethod;
+ }
+
+ @Override
+ public Method getJavaMember() {
+ return decoratedMethod.getJavaMember();
+ }
+
+ @Override
+ public boolean isStatic() {
+ return decoratedMethod.isStatic();
+ }
+
+ @Override
+ public AnnotatedType<X> getDeclaringType() {
+ return decoratedMethod.getDeclaringType();
+ }
+
+ @Override
+ public List<AnnotatedParameter<X>> getParameters() {
+ return decoratedMethod.getParameters();
+ }
+
+ @Override
+ public String toString() {
+ return decoratedMethod.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return decoratedMethod.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ return decoratedMethod.equals(object);
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/AnnotatedTypeDecorator.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/AnnotatedTypeDecorator.java
new file mode 100644
index 0000000..c02386e
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/AnnotatedTypeDecorator.java
@@ -0,0 +1,80 @@
+/*
+ * 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.cdi;
+
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.enterprise.inject.spi.AnnotatedConstructor;
+import javax.enterprise.inject.spi.AnnotatedField;
+import javax.enterprise.inject.spi.AnnotatedMethod;
+import javax.enterprise.inject.spi.AnnotatedType;
+
+final class AnnotatedTypeDecorator<X> extends AnnotatedDecorator implements AnnotatedType<X> {
+
+ private final AnnotatedType<X> decoratedType;
+
+ private final Set<AnnotatedMethod<? super X>> decoratedMethods;
+
+ AnnotatedTypeDecorator(AnnotatedType<X> decoratedType, Annotation decoratingAnnotation) {
+ this(decoratedType, decoratingAnnotation, Collections.<AnnotatedMethod<? super X>> emptySet());
+ }
+
+ AnnotatedTypeDecorator(AnnotatedType<X> decoratedType, Annotation decoratingAnnotation,
+ Set<AnnotatedMethod<? super X>> decoratedMethods) {
+ this(decoratedType, Collections.singleton(decoratingAnnotation), decoratedMethods);
+ }
+
+ AnnotatedTypeDecorator(AnnotatedType<X> decoratedType, Set<AnnotatedMethod<? super X>> decoratedMethods) {
+ this(decoratedType, Collections.<Annotation> emptySet(), decoratedMethods);
+ }
+
+ AnnotatedTypeDecorator(AnnotatedType<X> decoratedType, Set<Annotation> decoratingAnnotations,
+ Set<AnnotatedMethod<? super X>> decoratedMethods) {
+ super(decoratedType, decoratingAnnotations);
+ this.decoratedType = decoratedType;
+ this.decoratedMethods = decoratedMethods;
+ }
+
+ @Override
+ public Class<X> getJavaClass() {
+ return decoratedType.getJavaClass();
+ }
+
+ @Override
+ public Set<AnnotatedConstructor<X>> getConstructors() {
+ return decoratedType.getConstructors();
+ }
+
+ @Override
+ public Set<AnnotatedMethod<? super X>> getMethods() {
+ Set<AnnotatedMethod<? super X>> methods = new HashSet<>(decoratedType.getMethods());
+ for (AnnotatedMethod<? super X> method : decoratedMethods) {
+ methods.remove(method);
+ methods.add(method);
+ }
+
+ return Collections.unmodifiableSet(methods);
+ }
+
+ @Override
+ public Set<AnnotatedField<? super X>> getFields() {
+ return decoratedType.getFields();
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/AnyLiteral.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/AnyLiteral.java
new file mode 100644
index 0000000..7f3867a
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/AnyLiteral.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.cdi;
+
+import javax.enterprise.inject.Any;
+import javax.enterprise.util.AnnotationLiteral;
+
+final class AnyLiteral extends AnnotationLiteral<Any> implements Any {
+
+ static final Any INSTANCE = new AnyLiteral();
+
+ private static final long serialVersionUID = 1L;
+
+ private AnyLiteral() {
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/Beans.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/Beans.java
new file mode 100644
index 0000000..65ee3aa
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/Beans.java
@@ -0,0 +1,97 @@
+/*
+ * 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.cdi;
+
+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;
+
+/**
+ * Annotation to be used to customise the deployment configured by the {@code CamelCdiRunner}.
+ *
+ * @see CamelCdiRunner
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Beans {
+
+ /**
+ * Returns the list of <a href="http://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#alternatives">alternatives</a> to
+ * be selected in the application.
+ * <p/>
+ * Note that the declared alternatives are globally selected for the entire application. For example, if you have
+ * the following named bean in your application:
+ *
+ * <pre>
+ * <code>
+ * {@literal @}Named("foo")
+ * public class FooBean {
+ *
+ * }
+ * </code>
+ * </pre>
+ *
+ * It can be replaced in your test by declaring the following alternative bean:
+ *
+ * <pre>
+ * <code>
+ * {@literal @}Alternative
+ * {@literal @}Named("foo")
+ * public class AlternativeBean {
+ *
+ * }
+ * </code>
+ * </pre>
+ *
+ * And adding the {@code @Beans} annotation to you test class to activate it:
+ *
+ * <pre>
+ * <code>
+ * {@literal @}RunWith(CamelCdiRunner.class)
+ * {@literal @}Beans(alternatives = AlternativeBean.class)
+ * public class TestWithAlternative {
+ *
+ * }
+ * </code>
+ * </pre>
+ *
+ * @see javax.enterprise.inject.Alternative
+ */
+ Class<?>[] alternatives() default {};
+
+ /**
+ * Returns the list of classes to be added as beans in the application.
+ *
+ * That can be used to add classes to the deployment for test purpose in addition to the test class which is
+ * automatically added as bean.
+ *
+ */
+ Class<?>[] classes() default {};
+
+ /**
+ * Returns the list of classes whose packages are to be added for beans discovery.
+ *
+ * That can be used to add packages to the deployment for test purpose in addition to the test class which is
+ * automatically added as bean.
+ */
+ Class<?>[] packages() default {};
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/CamelCdiContext.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/CamelCdiContext.java
new file mode 100644
index 0000000..2b7d386
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/CamelCdiContext.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.cdi;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.enterprise.inject.spi.BeanManager;
+
+final class CamelCdiContext {
+
+ private final AtomicReference<BeanManager> manager = new AtomicReference<>();
+
+ BeanManager getBeanManager() {
+ BeanManager manager = this.manager.get();
+ if (manager == null) {
+ throw new IllegalStateException("Bean manager is not set!");
+ }
+ return manager;
+ }
+
+ void setBeanManager(BeanManager manager) {
+ if (!this.manager.compareAndSet(null, manager)) {
+ throw new IllegalStateException("Bean manager already set!");
+ }
+ }
+
+ void unsetBeanManager() {
+ if (manager.getAndSet(null) == null) {
+ throw new IllegalStateException("Bean manager is not set!");
+ }
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/CamelCdiDeployment.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/CamelCdiDeployment.java
new file mode 100644
index 0000000..e46c26f
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/CamelCdiDeployment.java
@@ -0,0 +1,82 @@
+/*
+ * 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.cdi;
+
+import org.apache.camel.cdi.CdiCamelExtension;
+import org.jboss.weld.config.ConfigurationKey;
+import org.jboss.weld.environment.se.Weld;
+import org.jboss.weld.environment.se.WeldContainer;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+
+final class CamelCdiDeployment implements TestRule {
+
+ private final CamelCdiContext context;
+
+ private final Weld weld;
+
+ CamelCdiDeployment(TestClass test, CamelCdiContext context) {
+ this.context = context;
+
+ weld = new Weld()
+ // TODO: check parallel execution
+ .containerId("camel-context-cdi")
+ .property(ConfigurationKey.RELAXED_CONSTRUCTION.get(), true)
+ .property(Weld.SHUTDOWN_HOOK_SYSTEM_PROPERTY, false)
+ .enableDiscovery()
+ .beanClasses(test.getJavaClass().getDeclaredClasses())
+ .addBeanClass(test.getJavaClass())
+ .addExtension(new CdiCamelExtension());
+
+ // Apply deployment customization provided by the @Beans annotation
+ // if present on the test class
+ if (test.getJavaClass().isAnnotationPresent(Beans.class)) {
+ Beans beans = test.getJavaClass().getAnnotation(Beans.class);
+ weld.addExtension(new CamelCdiTestExtension(beans));
+ for (Class<?> alternative : beans.alternatives()) {
+ // It is not necessary to add the alternative class with WELD-2218
+ // anymore, though it's kept for previous versions
+ weld.addBeanClass(alternative)
+ .addAlternative(alternative);
+ }
+ for (Class<?> clazz : beans.classes()) {
+ weld.addBeanClass(clazz);
+ }
+ weld.addPackages(false, beans.packages());
+ }
+ }
+
+ @Override
+ public Statement apply(final Statement base, Description description) {
+ return new Statement() {
+
+ @Override
+ public void evaluate() throws Throwable {
+ WeldContainer container = weld.initialize();
+ context.setBeanManager(container.getBeanManager());
+ try {
+ base.evaluate();
+ } finally {
+ container.shutdown();
+ context.unsetBeanManager();
+ }
+ }
+ };
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/CamelCdiRunner.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/CamelCdiRunner.java
new file mode 100644
index 0000000..d64a624
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/CamelCdiRunner.java
@@ -0,0 +1,98 @@
+/*
+ * 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.cdi;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+
+import org.junit.rules.TestRule;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+
+public class CamelCdiRunner extends BlockJUnit4ClassRunner {
+
+ private final CamelCdiContext context = new CamelCdiContext();
+
+ public CamelCdiRunner(Class<?> clazz) throws InitializationError {
+ super(clazz);
+ }
+
+ @Override
+ protected void validateConstructor(List<Throwable> errors) {
+ // The test class is instantiated as a CDI bean so we bypass the
+ // default JUnit test class constructor constraints validation.
+ }
+
+ @Override
+ protected void validatePublicVoidNoArgMethods(
+ Class<? extends Annotation> annotation, boolean isStatic, List<Throwable> errors) {
+ // Overrides the default validation to allow test methods with
+ // parameters so that we can inject CDI references.
+ List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(annotation);
+ for (FrameworkMethod eachTestMethod : methods) {
+ eachTestMethod.validatePublicVoid(isStatic, errors);
+ }
+ }
+
+ @Override
+ protected List<FrameworkMethod> getChildren() {
+ List<FrameworkMethod> children = super.getChildren();
+ boolean hasDefinedOrder = false;
+ for (FrameworkMethod method : children) {
+ if (method.getAnnotation(Order.class) != null) {
+ hasDefinedOrder = true;
+ }
+ }
+ if (hasDefinedOrder) {
+ List<FrameworkMethod> sorted = new ArrayList<>(children);
+ Collections.sort(sorted, new FrameworkMethodSorter());
+ return sorted;
+ }
+ return children;
+ }
+
+ @Override
+ protected List<TestRule> classRules() {
+ List<TestRule> rules = super.classRules();
+ // Add the CDI container rule before all the other class rules
+ // so that it's the last one in FIFO
+ rules.add(0, new CamelCdiDeployment(getTestClass(), context));
+ return rules;
+ }
+
+ @Override
+ protected Object createTest() {
+ BeanManager manager = context.getBeanManager();
+ Set<Bean<?>> beans = manager.getBeans(getTestClass().getJavaClass(), AnyLiteral.INSTANCE);
+ Bean<?> bean = beans.iterator().next();
+ // TODO: manage lifecycle of @Dependent beans
+ return manager.getReference(bean, bean.getBeanClass(), manager.createCreationalContext(bean));
+ }
+
+ @Override
+ protected Statement methodInvoker(FrameworkMethod method, Object test) {
+ return new FrameworkMethodCdiInjection(method, test, context);
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/CamelCdiTestExtension.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/CamelCdiTestExtension.java
new file mode 100644
index 0000000..fa5b763
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/CamelCdiTestExtension.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.cdi;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.annotation.Priority;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.Alternative;
+import javax.enterprise.inject.spi.AnnotatedMethod;
+import javax.enterprise.inject.spi.AnnotatedType;
+import javax.enterprise.inject.spi.Extension;
+import javax.enterprise.inject.spi.ProcessAnnotatedType;
+import javax.enterprise.inject.spi.WithAnnotations;
+
+import static javax.interceptor.Interceptor.Priority.APPLICATION;
+
+final class CamelCdiTestExtension implements Extension {
+
+ private final Beans beans;
+
+ CamelCdiTestExtension(Beans beans) {
+ this.beans = beans;
+ }
+
+ /**
+ * Activates the alternatives declared with {@code @Beans} globally for the application.
+ * <p/>
+ * For every types and every methods of every types declared with {@link Beans#alternatives()}, the {@code Priority}
+ * annotation is added so that the corresponding alternatives are selected globally for the entire application.
+ *
+ * @see Beans
+ */
+ private <T> void alternatives(@Observes @WithAnnotations(Alternative.class) ProcessAnnotatedType<T> pat) {
+ AnnotatedType<T> type = pat.getAnnotatedType();
+
+ if (!Arrays.asList(beans.alternatives()).contains(type.getJavaClass())) {
+ // Only select globally the alternatives that are declared with @Beans
+ return;
+ }
+
+ Set<AnnotatedMethod<? super T>> methods = new HashSet<>();
+ for (AnnotatedMethod<? super T> method : type.getMethods()) {
+ if (method.isAnnotationPresent(Alternative.class) && !method.isAnnotationPresent(Priority.class)) {
+ methods.add(new AnnotatedMethodDecorator<>(method, PriorityLiteral.of(APPLICATION)));
+ }
+ }
+
+ if (type.isAnnotationPresent(Alternative.class) && !type.isAnnotationPresent(Priority.class)) {
+ pat.setAnnotatedType(new AnnotatedTypeDecorator<>(type, PriorityLiteral.of(APPLICATION), methods));
+ } else if (!methods.isEmpty()) {
+ pat.setAnnotatedType(new AnnotatedTypeDecorator<>(type, methods));
+ }
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/FrameworkAnnotatedParameter.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/FrameworkAnnotatedParameter.java
new file mode 100644
index 0000000..dee1121
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/FrameworkAnnotatedParameter.java
@@ -0,0 +1,91 @@
+/*
+ * 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.cdi;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.enterprise.inject.spi.AnnotatedCallable;
+import javax.enterprise.inject.spi.AnnotatedParameter;
+import javax.enterprise.inject.spi.BeanManager;
+
+final class FrameworkAnnotatedParameter<X> implements AnnotatedParameter<X> {
+
+ private final int position;
+
+ private final Type type;
+
+ private final Set<Annotation> annotations;
+
+ private final BeanManager manager;
+
+ FrameworkAnnotatedParameter(Method method, int position, BeanManager manager) {
+ this.position = position;
+ this.type = method.getGenericParameterTypes()[position];
+ this.annotations = new HashSet<>(Arrays.asList(method.getParameterAnnotations()[position]));
+ this.manager = manager;
+ }
+
+ @Override
+ public AnnotatedCallable<X> getDeclaringCallable() {
+ return null;
+ }
+
+ @Override
+ public int getPosition() {
+ return position;
+ }
+
+ @Override
+ public <Y extends Annotation> Y getAnnotation(Class<Y> type) {
+ for (Annotation annotation : getAnnotations()) {
+ if (annotation.annotationType() == type) {
+ return type.cast(annotation);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Set<Annotation> getAnnotations() {
+ return Collections.unmodifiableSet(annotations);
+ }
+
+ @Override
+ public Type getBaseType() {
+ return type;
+ }
+
+ @Override
+ public Set<Type> getTypeClosure() {
+ if (type instanceof Class) {
+ return manager.createAnnotatedType((Class) type).getTypeClosure();
+ } else {
+ return Collections.singleton(type);
+ }
+ }
+
+ @Override
+ public boolean isAnnotationPresent(Class<? extends Annotation> type) {
+ return getAnnotation(type) != null;
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/FrameworkMethodCdiInjection.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/FrameworkMethodCdiInjection.java
new file mode 100644
index 0000000..d23095b
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/FrameworkMethodCdiInjection.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.cdi;
+
+import java.lang.reflect.Type;
+
+import javax.enterprise.inject.spi.BeanManager;
+
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+final class FrameworkMethodCdiInjection extends Statement {
+
+ private final FrameworkMethod method;
+
+ private final Object test;
+
+ private final CamelCdiContext context;
+
+ FrameworkMethodCdiInjection(FrameworkMethod method, Object test, CamelCdiContext context) {
+ this.method = method;
+ this.test = test;
+ this.context = context;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+ BeanManager manager = context.getBeanManager();
+ Type[] types = method.getMethod().getGenericParameterTypes();
+ Object[] parameters = new Object[types.length];
+ for (int i = 0; i < types.length; i++) {
+ // TODO: use a proper CreationalContext...
+ parameters[i] = manager.getInjectableReference(new FrameworkMethodInjectionPoint(method.getMethod(), i, manager),
+ manager.createCreationalContext(null));
+ }
+
+ method.invokeExplosively(test, parameters);
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/FrameworkMethodInjectionPoint.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/FrameworkMethodInjectionPoint.java
new file mode 100644
index 0000000..ec79490
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/FrameworkMethodInjectionPoint.java
@@ -0,0 +1,88 @@
+/*
+ * 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.cdi;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.enterprise.inject.spi.Annotated;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.InjectionPoint;
+
+final class FrameworkMethodInjectionPoint implements InjectionPoint {
+
+ private final Method method;
+
+ private final FrameworkAnnotatedParameter annotated;
+
+ private final Type type;
+
+ private final Set<Annotation> qualifiers;
+
+ FrameworkMethodInjectionPoint(Method method, int position, BeanManager manager) {
+ this.method = method;
+ this.annotated = new FrameworkAnnotatedParameter(method, position, manager);
+ this.type = method.getGenericParameterTypes()[position];
+ this.qualifiers = new HashSet<>();
+ for (Annotation annotation : method.getParameterAnnotations()[position]) {
+ if (manager.isQualifier(annotation.annotationType())) {
+ qualifiers.add(annotation);
+ }
+ }
+ }
+
+ @Override
+ public Bean<?> getBean() {
+ return null;
+ }
+
+ @Override
+ public Member getMember() {
+ return method;
+ }
+
+ @Override
+ public Set<Annotation> getQualifiers() {
+ return Collections.unmodifiableSet(qualifiers);
+ }
+
+ @Override
+ public Type getType() {
+ return type;
+ }
+
+ @Override
+ public boolean isDelegate() {
+ return false;
+ }
+
+ @Override
+ public boolean isTransient() {
+ return false;
+ }
+
+ @Override
+ public Annotated getAnnotated() {
+ return annotated;
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/FrameworkMethodSorter.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/FrameworkMethodSorter.java
new file mode 100644
index 0000000..6820ec7
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/FrameworkMethodSorter.java
@@ -0,0 +1,37 @@
+/*
+ * 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.cdi;
+
+import java.util.Comparator;
+
+import org.junit.runners.model.FrameworkMethod;
+
+final class FrameworkMethodSorter implements Comparator<FrameworkMethod> {
+
+ @Override
+ public int compare(FrameworkMethod m1, FrameworkMethod m2) {
+ int i1 = 0;
+ int i2 = 0;
+ if (m1.getMethod().isAnnotationPresent(Order.class)) {
+ i1 = m1.getMethod().getAnnotation(Order.class).value();
+ }
+ if (m2.getMethod().isAnnotationPresent(Order.class)) {
+ i2 = m2.getMethod().getAnnotation(Order.class).value();
+ }
+ return i1 < i2 ? -1 : (i1 == i2 ? 0 : 1);
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/Order.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/Order.java
new file mode 100644
index 0000000..4acf98c
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/Order.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.cdi;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Order {
+
+ int value();
+
+}
diff --git a/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/PriorityLiteral.java b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/PriorityLiteral.java
new file mode 100644
index 0000000..2f6ff6f
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/main/java/org/apache/camel/test/cdi/PriorityLiteral.java
@@ -0,0 +1,38 @@
+/*
+ * 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.cdi;
+
+import javax.annotation.Priority;
+import javax.enterprise.util.AnnotationLiteral;
+
+final class PriorityLiteral extends AnnotationLiteral<Priority> implements Priority {
+
+ private final int priority;
+
+ private PriorityLiteral(int priority) {
+ this.priority = priority;
+ }
+
+ static PriorityLiteral of(int priority) {
+ return new PriorityLiteral(priority);
+ }
+
+ @Override
+ public int value() {
+ return priority;
+ }
+}
diff --git a/components/camel-test/camel-test-cdi/src/test/java/org/apache/camel/test/cdi/FilterTest.java b/components/camel-test/camel-test-cdi/src/test/java/org/apache/camel/test/cdi/FilterTest.java
new file mode 100644
index 0000000..4171106
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/test/java/org/apache/camel/test/cdi/FilterTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.cdi;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+// START SNIPPET: example
+// tag::example[]
+@RunWith(CamelCdiRunner.class)
+public class FilterTest {
+
+ @EndpointInject("mock:result")
+ protected MockEndpoint resultEndpoint;
+
+ @Produce("direct:start")
+ protected ProducerTemplate template;
+
+ @Before
+ public void before() {
+ resultEndpoint.reset();
+ }
+
+ @Test
+ public void testSendMatchingMessage() throws Exception {
+ String expectedBody = "<matched/>";
+
+ resultEndpoint.expectedBodiesReceived(expectedBody);
+
+ template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+ resultEndpoint.assertIsSatisfied();
+ }
+
+ @Test
+ public void testSendNotMatchingMessage() throws Exception {
+ resultEndpoint.expectedMessageCount(0);
+
+ template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
+
+ resultEndpoint.assertIsSatisfied();
+ }
+
+ static class ContextConfig extends RouteBuilder {
+
+ @Override
+ public void configure() {
+ from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+ }
+ }
+}
+// end::example[]
+// END SNIPPET: example
diff --git a/components/camel-test/camel-test-cdi/src/test/resources/log4j2.properties b/components/camel-test/camel-test-cdi/src/test/resources/log4j2.properties
new file mode 100644
index 0000000..290318f
--- /dev/null
+++ b/components/camel-test/camel-test-cdi/src/test/resources/log4j2.properties
@@ -0,0 +1,28 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements. See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-test-cdi.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d %-5p %c{1} - %m %n
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
diff --git a/components/camel-test/camel-test-junit5/pom.xml b/components/camel-test/camel-test-junit5/pom.xml
new file mode 100644
index 0000000..f24fe00
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/pom.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test-parent</artifactId>
+ <version>3.9.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-test-junit5</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Camel :: Test :: JUnit5</name>
+ <description>Camel unit testing with JUnit 5</description>
+
+ <properties>
+ <firstVersion>3.0.0</firstVersion>
+ <label>testing,java</label>
+ <title>Test JUnit5</title>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-core-engine</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-core-languages</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-management</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- common deps used in tests -->
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-bean</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-browse</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-controlbus</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-dataformat</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-dataset</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-direct</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-directvm</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-file</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-language</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-log</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-mock</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-ref</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-rest</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-saga</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-scheduler</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-seda</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-stub</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-timer</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-validator</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-vm</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-xpath</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-xslt</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-params</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-slf4j-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-xml-jaxb</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+</project>
diff --git a/components/camel-test/camel-test-junit5/src/generated/resources/META-INF/services/org/apache/camel/other.properties b/components/camel-test/camel-test-junit5/src/generated/resources/META-INF/services/org/apache/camel/other.properties
new file mode 100644
index 0000000..1ad70bb
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/generated/resources/META-INF/services/org/apache/camel/other.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+name=test-junit5
+groupId=org.apache.camel
+artifactId=camel-test-junit5
+version=3.9.0-SNAPSHOT
+projectName=Camel :: Test :: JUnit5
+projectDescription=Camel unit testing with JUnit 5
diff --git a/components/camel-test/camel-test-junit5/src/generated/resources/test-junit5.json b/components/camel-test/camel-test-junit5/src/generated/resources/test-junit5.json
new file mode 100644
index 0000000..65ebd04
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/generated/resources/test-junit5.json
@@ -0,0 +1,15 @@
+{
+ "other": {
+ "kind": "other",
+ "name": "test-junit5",
+ "title": "Test JUnit5",
+ "description": "Camel unit testing with JUnit 5",
+ "deprecated": false,
+ "firstVersion": "3.0.0",
+ "label": "testing,java",
+ "supportLevel": "Stable",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-test-junit5",
+ "version": "3.9.0-SNAPSHOT"
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/docs/test-junit5.adoc b/components/camel-test/camel-test-junit5/src/main/docs/test-junit5.adoc
new file mode 100644
index 0000000..dac0b78
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/docs/test-junit5.adoc
@@ -0,0 +1,93 @@
+[[test-junit5-other]]
+= Test JUnit5 Component
+:docTitle: Test JUnit5
+:artifactId: camel-test-junit5
+:description: Camel unit testing with JUnit 5
+:since: 3.0
+:supportLevel: Stable
+include::{cq-version}@camel-quarkus:ROOT:partial$reference/others/test-junit5.adoc[opts=optional]
+
+*Since Camel {since}*
+
+The `camel-test-junit5` module is used for unit testing Camel.
+
+The class `org.apache.camel.test.junit5.CamelTestSupport` provides a base JUnit class which you would extend
+and implement your Camel unit test.
+
+== Simple unit test example
+
+As shown below is a basic junit test which uses `camel-test-junit5`. The `createRouteBuilder` method is used
+for build the routes to be tested. Then the methods with `@Test` annotations are JUnit test methods which
+will be executed. The base class `CamelTestSupport` has a number of helper methods to configure testing,
+see more at the javadoc of this class.
+
+[source,java]
+----
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class SimpleMockTest extends CamelTestSupport {
+
+ @Test
+ public void testMock() throws Exception {
+ getMockEndpoint("mock:result").expectedBodiesReceived("Hello World");
+
+ template.sendBody("direct:start", "Hello World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start").to("mock:result");
+ }
+ };
+ }
+
+}
+----
+
+== Migrating Camel Tests from JUnit 4 to JUnit 5
+Find below some hints to help in migrating camel tests from JUnit 4 to JUnit 5.
+
+=== Referencing the Camel Test JUnit5 library in your project
+Projects using `camel-test` would need to use `camel-test-junit5`. For instance, maven users would update their pom.xml file as below:
+----
+<dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test-junit5</artifactId>
+ <scope>test</scope>
+</dependency>
+----
+
+Tips: It's possible to run JUnit4 & JUnit5 based camel tests side by side including the following dependencies `camel-test`,
+`camel-test-junit5` and `junit-vintage-engine`. This configuration allows to migrate a camel test at once.
+
+=== Typical migration steps linked to JUnit 5 support in Camel Test
+* Imports of `org.apache.camel.test.junit4.\*` should be replaced with `org.apache.camel.test.junit5.*`
+* `TestSupport` static methods should be imported where needed, for instance `import static org.apache.camel.test.junit5.TestSupport.assertIsInstanceOf`
+* Usage of the field `CamelTestSupport.log` should be replaced by another logger, for instance `org.slf4j.LoggerFactory.getLogger(MyCamelTest.class);`
+* Usage of the method `CamelTestSupport.createRegistry` should be replaced by `CamelTestSupport.createCamelRegistry()`
+* Overrides of `isCreateCamelContextPerClass()` returning `false` should be removed
+* Overrides of `isCreateCamelContextPerClass()` returning `true` should be replaced by `@TestInstance(Lifecycle.PER_CLASS)`
+
+
+=== Typical migration steps linked to JUnit 5 itself
+Once Camel related steps have been performed, there are still typical JUnit 5 migration steps to remember:
+
+* New JUnit 5 assertions should be imported where needed, for instance `import static org.junit.jupiter.api.Assertions.assertEquals`
+* Assertion messages should be moved to the last parameter where needed, for instance `assertEquals("message", 2, 1)` becomes `assertEquals(2, 1, "message")`
+* `org.junit.Test` should be changed in favor of `org.junit.jupiter.api.Test`
+* `org.junit.Ignore` should be changed in favor of `org.junit.jupiter.api.Disabled`
+* `org.junit.Before` should be changed in favor of `org.junit.jupiter.api.BeforeEach`
+* `org.junit.After` should be changed in favor of `org.junit.jupiter.api.AfterEach`
+* `org.junit.BeforeClass` should be changed in favor of `import org.junit.jupiter.api.BeforeAll`
+* `org.junit.AfterClass` should be changed in favor of `import org.junit.jupiter.api.AfterAll`
+* Built-in `assertThat` from third-party assertion libraries should be used. For instance, use `org.hamcrest.MatcherAssert.assertThat` from `java-hamcrest`
+
+Depending on the context, more involved tips might be needed, please check the https://junit.org/junit5/docs/current/user-guide/[JUnit 5 User Guide] then.
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/AvailablePortFinder.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/AvailablePortFinder.java
new file mode 100644
index 0000000..3adc24d
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/AvailablePortFinder.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;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Finds currently available server ports.
+ */
+public final class AvailablePortFinder {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AvailablePortFinder.class);
+
+ /**
+ * Creates a new instance.
+ */
+ private AvailablePortFinder() {
+ // Do nothing
+ }
+
+ /**
+ * Gets the next available port.
+ *
+ * @throws IllegalStateException if there are no ports available
+ * @return the available port
+ */
+ public static int getNextAvailable() {
+ return probePort(0);
+ }
+
+ /**
+ * Gets the next available port in the given range.
+ *
+ * @param fromPort port number start range.
+ * @param toPort port number end range.
+ *
+ * @throws IllegalStateException if there are no ports available
+ * @return the available port
+ */
+ public static int getNextAvailable(int fromPort, int toPort) {
+ for (int i = fromPort; i <= toPort; i++) {
+ try {
+ return probePort(i);
+ } catch (IllegalStateException e) {
+ // do nothing, let's try the next port
+ }
+ }
+ throw new IllegalStateException("Cannot find free port");
+ }
+
+ /**
+ * Probe a port to see if it is free
+ *
+ * @param port an integer port number to be tested. If port is 0, then the next available port is
+ * returned.
+ * @throws IllegalStateException if the port is not free or, in case of port 0, if there are no ports available at
+ * all.
+ * @return the port number itself if the port is free or, in case of port 0, the first
+ * available port number.
+ */
+ private static int probePort(int port) {
+ try (ServerSocket ss = new ServerSocket()) {
+ ss.setReuseAddress(true);
+ ss.bind(new InetSocketAddress((InetAddress) null, port), 1);
+ int probedPort = ss.getLocalPort();
+ LOG.info("Available port is -> {}", probedPort);
+ return probedPort;
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot find free port", e);
+ }
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/AvailablePortFinderPropertiesFunction.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/AvailablePortFinderPropertiesFunction.java
new file mode 100644
index 0000000..078f0d6
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/AvailablePortFinderPropertiesFunction.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.camel.spi.PropertiesFunction;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
+
+/**
+ * A {@link PropertiesFunction} that reserves network ports and place them in a cache for reuse.
+ * <p/>
+ * The first time the function is invoked for a given name, an unused network port is determined and cached in an hash
+ * map with the given name as key so each time this function is invoked for the same name, the previously discovered
+ * port will be returned.
+ * <p/>
+ * This is useful for testing purpose where you can write a route like:
+ *
+ * <pre>
+ * {@code
+ * from("undertow:http://0.0.0.0:{{available-port:server-port}}")
+ * .to("mock:result");
+ * }
+ * </pre>
+ *
+ * And then you can invoke with {@link org.apache.camel.ProducerTemplate} like:
+ *
+ * <pre>
+ * {@code
+ * template.sendBody("undertow:http://0.0.0.0:{{available-port:server-port}}", "the body");
+ * }
+ * </pre>
+ *
+ * Doing so avoid the need to compute the port and pass it to the various method or store it as a global variable in the
+ * test class.
+ *
+ * @see AvailablePortFinder
+ */
+public class AvailablePortFinderPropertiesFunction implements PropertiesFunction {
+ private final Map<String, String> portMapping;
+
+ public AvailablePortFinderPropertiesFunction() {
+ this.portMapping = new ConcurrentHashMap<>();
+ }
+
+ @Override
+ public String getName() {
+ return "available-port";
+ }
+
+ @Override
+ public String apply(String remainder) {
+ if (ObjectHelper.isEmpty(remainder)) {
+ return remainder;
+ }
+
+ String name = StringHelper.before(remainder, ":");
+ String range = StringHelper.after(remainder, ":");
+
+ if (name == null) {
+ name = remainder;
+ }
+
+ final Integer from;
+ final Integer to;
+
+ if (range != null) {
+ String f = StringHelper.before(range, "-");
+ if (ObjectHelper.isEmpty(f)) {
+ throw new IllegalArgumentException(
+ "Unable to parse from range, range should be defined in the as from-to, got: " + range);
+ }
+
+ String t = StringHelper.after(range, "-");
+ if (ObjectHelper.isEmpty(t)) {
+ throw new IllegalArgumentException(
+ "Unable to parse to range, range should be defined in the as from-to, got: " + range);
+ }
+
+ from = Integer.parseInt(f);
+ to = Integer.parseInt(t);
+ } else {
+ from = null;
+ to = null;
+ }
+
+ return this.portMapping.computeIfAbsent(name, n -> {
+ final int port;
+
+ if (from != null && to != null) {
+ port = AvailablePortFinder.getNextAvailable(from, to);
+ } else {
+ port = AvailablePortFinder.getNextAvailable();
+ }
+
+ return Integer.toString(port);
+ });
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/CamelRouteCoverageDumper.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/CamelRouteCoverageDumper.java
new file mode 100644
index 0000000..a04d9fd
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/CamelRouteCoverageDumper.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+
+import org.apache.camel.Route;
+import org.apache.camel.api.management.ManagedCamelContext;
+import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
+import org.apache.camel.api.management.mbean.ManagedProcessorMBean;
+import org.apache.camel.api.management.mbean.ManagedRouteMBean;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.util.IOHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A <code>CamelRouteCoverageDumper</code> instance dumps the route coverage of a given camel test.
+ */
+public class CamelRouteCoverageDumper {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CamelRouteCoverageDumper.class);
+
+ public void dump(
+ ManagedCamelContextMBean managedCamelContext, ModelCamelContext context, String dumpDir, String dumpFilename,
+ String testClass, String testName,
+ long testTimeTaken)
+ throws Exception {
+ logCoverageSummary(managedCamelContext, context);
+
+ String routeCoverageAsXml = managedCamelContext.dumpRoutesCoverageAsXml();
+ String combined = "<camelRouteCoverage>\n" + gatherTestDetailsAsXml(testClass, testName, testTimeTaken)
+ + routeCoverageAsXml + "\n</camelRouteCoverage>";
+
+ File dumpFile = new File(dumpDir);
+ // ensure dir exists
+ dumpFile.mkdirs();
+ dumpFile = new File(dumpDir, dumpFilename);
+
+ LOG.info("Dumping route coverage to file: {}", dumpFile);
+ InputStream is = new ByteArrayInputStream(combined.getBytes());
+ OutputStream os = new FileOutputStream(dumpFile, false);
+ IOHelper.copyAndCloseInput(is, os);
+ IOHelper.close(os);
+ }
+
+ /**
+ * Groups all processors from Camel context by route id.
+ */
+ private Map<String, List<ManagedProcessorMBean>> findProcessorsForEachRoute(MBeanServer server, ModelCamelContext context)
+ throws MalformedObjectNameException, MBeanException, AttributeNotFoundException, InstanceNotFoundException,
+ ReflectionException {
+ String domain = context.getManagementStrategy().getManagementAgent().getMBeanServerDefaultDomain();
+
+ Map<String, List<ManagedProcessorMBean>> processorsForRoute = new HashMap<>();
+
+ ObjectName processorsObjectName
+ = new ObjectName(domain + ":context=" + context.getManagementName() + ",type=processors,name=*");
+ Set<ObjectName> objectNames = server.queryNames(processorsObjectName, null);
+
+ for (ObjectName objectName : objectNames) {
+ String routeId = server.getAttribute(objectName, "RouteId").toString();
+ String name = objectName.getKeyProperty("name");
+ name = ObjectName.unquote(name);
+
+ ManagedProcessorMBean managedProcessor = context.getExtension(ManagedCamelContext.class).getManagedProcessor(name);
+
+ if (managedProcessor != null) {
+ if (processorsForRoute.get(routeId) == null) {
+ List<ManagedProcessorMBean> processorsList = new ArrayList<>();
+ processorsList.add(managedProcessor);
+
+ processorsForRoute.put(routeId, processorsList);
+ } else {
+ processorsForRoute.get(routeId).add(managedProcessor);
+ }
+ }
+ }
+
+ // sort processors by position in route definition
+ for (Map.Entry<String, List<ManagedProcessorMBean>> entry : processorsForRoute.entrySet()) {
+ Collections.sort(entry.getValue(), Comparator.comparing(ManagedProcessorMBean::getIndex));
+ }
+
+ return processorsForRoute;
+ }
+
+ /**
+ * Gathers test details as xml.
+ */
+ private String gatherTestDetailsAsXml(String testClass, String testName, long timeTaken) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("<test>\n");
+ sb.append(" <class>").append(testClass).append("</class>\n");
+ sb.append(" <method>").append(testName).append("</method>\n");
+ sb.append(" <time>").append(timeTaken).append("</time>\n");
+ sb.append("</test>\n");
+ return sb.toString();
+ }
+
+ /**
+ * Logs route coverage summary, including which routes are uncovered and what is the coverage of each processor in
+ * each route.
+ */
+ private void logCoverageSummary(ManagedCamelContextMBean managedCamelContext, ModelCamelContext context) throws Exception {
+ StringBuilder builder = new StringBuilder("\nCoverage summary\n");
+
+ int routes = managedCamelContext.getTotalRoutes();
+
+ long contextExchangesTotal = managedCamelContext.getExchangesTotal();
+
+ List<String> uncoveredRoutes = new ArrayList<>();
+
+ StringBuilder routesSummary = new StringBuilder();
+ routesSummary.append("\tProcessor coverage\n");
+
+ MBeanServer server = context.getManagementStrategy().getManagementAgent().getMBeanServer();
+
+ Map<String, List<ManagedProcessorMBean>> processorsForRoute = findProcessorsForEachRoute(server, context);
+
+ // log processor coverage for each route
+ for (Route route : context.getRoutes()) {
+ ManagedRouteMBean managedRoute = context.getExtension(ManagedCamelContext.class).getManagedRoute(route.getId());
+ if (managedRoute.getExchangesTotal() == 0) {
+ uncoveredRoutes.add(route.getId());
+ }
+
+ long routeCoveragePercentage = Math.round((double) managedRoute.getExchangesTotal() / contextExchangesTotal * 100);
+ routesSummary.append("\t\tRoute ").append(route.getId()).append(" total: ").append(managedRoute.getExchangesTotal())
+ .append(" (").append(routeCoveragePercentage)
+ .append("%)\n");
+
+ if (server != null) {
+ List<ManagedProcessorMBean> processors = processorsForRoute.get(route.getId());
+ if (processors != null) {
+ for (ManagedProcessorMBean managedProcessor : processors) {
+ String processorId = managedProcessor.getProcessorId();
+ long processorExchangesTotal = managedProcessor.getExchangesTotal();
+ long processorCoveragePercentage
+ = Math.round((double) processorExchangesTotal / contextExchangesTotal * 100);
+ routesSummary.append("\t\t\tProcessor ").append(processorId).append(" total: ")
+ .append(processorExchangesTotal).append(" (")
+ .append(processorCoveragePercentage).append("%)\n");
+ }
+ }
+ }
+ }
+
+ int used = routes - uncoveredRoutes.size();
+
+ long contextPercentage = Math.round((double) used / routes * 100);
+ builder.append("\tRoute coverage: ").append(used).append(" out of ").append(routes).append(" routes used (")
+ .append(contextPercentage).append("%)\n");
+ builder.append("\t\tCamelContext (").append(managedCamelContext.getCamelId()).append(") total: ")
+ .append(contextExchangesTotal).append("\n");
+
+ if (uncoveredRoutes.size() > 0) {
+ builder.append("\t\tUncovered routes: ").append(uncoveredRoutes.stream().collect(Collectors.joining(", ")))
+ .append("\n");
+ }
+
+ builder.append(routesSummary);
+ LOG.info(builder.toString());
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/ExcludingPackageScanClassResolver.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/ExcludingPackageScanClassResolver.java
new file mode 100644
index 0000000..ce04f9e
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/ExcludingPackageScanClassResolver.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.camel.impl.engine.DefaultPackageScanClassResolver;
+import org.apache.camel.impl.scan.AssignableToPackageScanFilter;
+import org.apache.camel.impl.scan.InvertingPackageScanFilter;
+
+public class ExcludingPackageScanClassResolver extends DefaultPackageScanClassResolver {
+
+ public void setExcludedClasses(Set<Class<?>> excludedClasses) {
+ Set<Class<?>> parents = excludedClasses == null ? Collections.<Class<?>> emptySet() : excludedClasses;
+ addFilter(new InvertingPackageScanFilter(new AssignableToPackageScanFilter(parents)));
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/TestSupportNodeIdFactory.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/TestSupportNodeIdFactory.java
new file mode 100644
index 0000000..e66d42a
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/TestSupportNodeIdFactory.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test;
+
+import org.apache.camel.impl.engine.DefaultNodeIdFactory;
+
+public class TestSupportNodeIdFactory extends DefaultNodeIdFactory {
+
+ /**
+ * Start tests with fresh counters
+ */
+ public static void resetCounters() {
+ resetAllCounters();
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/CamelTestSupport.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/CamelTestSupport.java
new file mode 100644
index 0000000..258ecb2
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/CamelTestSupport.java
@@ -0,0 +1,1028 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5;
+
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ConsumerTemplate;
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.FluentProducerTemplate;
+import org.apache.camel.Message;
+import org.apache.camel.NamedNode;
+import org.apache.camel.NoSuchEndpointException;
+import org.apache.camel.Predicate;
+import org.apache.camel.Processor;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.Service;
+import org.apache.camel.ServiceStatus;
+import org.apache.camel.api.management.ManagedCamelContext;
+import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
+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.component.mock.MockEndpoint;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.impl.debugger.DefaultDebugger;
+import org.apache.camel.model.Model;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.spi.CamelBeanPostProcessor;
+import org.apache.camel.spi.Language;
+import org.apache.camel.spi.PropertiesComponent;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.support.BreakpointSupport;
+import org.apache.camel.support.EndpointHelper;
+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.apache.camel.util.URISupport;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * A useful base class which creates a {@link org.apache.camel.CamelContext} with some routes along with a
+ * {@link org.apache.camel.ProducerTemplate} for use in the test case Do <tt>not</tt> use this class for Spring Boot
+ * testing.
+ */
+public abstract class CamelTestSupport
+ implements BeforeEachCallback, AfterAllCallback, BeforeAllCallback, BeforeTestExecutionCallback,
+ AfterTestExecutionCallback {
+
+ /**
+ * JVM system property which can be set to true to turn on dumping route coverage statistics.
+ */
+ public static final String ROUTE_COVERAGE_ENABLED = "CamelTestRouteCoverage";
+
+ // CHECKSTYLE:OFF
+ private static final Logger LOG = LoggerFactory.getLogger(CamelTestSupport.class);
+ private static ThreadLocal<ModelCamelContext> threadCamelContext = new ThreadLocal<>();
+ private static ThreadLocal<ProducerTemplate> threadTemplate = new ThreadLocal<>();
+ private static ThreadLocal<FluentProducerTemplate> threadFluentTemplate = new ThreadLocal<>();
+ private static ThreadLocal<ConsumerTemplate> threadConsumer = new ThreadLocal<>();
+ private static ThreadLocal<Service> threadService = new ThreadLocal<>();
+ protected Properties extra;
+ protected volatile ModelCamelContext context;
+ protected volatile ProducerTemplate template;
+ protected volatile FluentProducerTemplate fluentTemplate;
+ protected volatile ConsumerTemplate consumer;
+ protected volatile Service camelContextService;
+ @RegisterExtension
+ protected CamelTestSupport camelTestSupportExtension = this;
+ private boolean useRouteBuilder = true;
+ private final DebugBreakpoint breakpoint = new DebugBreakpoint();
+ private final StopWatch watch = new StopWatch();
+ private final Map<String, String> fromEndpoints = new HashMap<>();
+ private static final ThreadLocal<AtomicInteger> TESTS = new ThreadLocal<>();
+ private static final ThreadLocal<CamelTestSupport> INSTANCE = new ThreadLocal<>();
+ private String currentTestName;
+ private boolean isCreateCamelContextPerClass = false;
+ private CamelRouteCoverageDumper routeCoverageDumper = new CamelRouteCoverageDumper();
+ // CHECKSTYLE:ON
+
+ @Override
+ public void afterTestExecution(ExtensionContext context) throws Exception {
+ watch.taken();
+ }
+
+ @Override
+ public void beforeTestExecution(ExtensionContext context) throws Exception {
+ watch.restart();
+ }
+
+ public long timeTaken() {
+ return watch.taken();
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ currentTestName = context.getDisplayName();
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext context) {
+ isCreateCamelContextPerClass
+ = context.getTestInstanceLifecycle().filter(lc -> lc.equals(Lifecycle.PER_CLASS)).isPresent();
+ }
+
+ @Override
+ public void afterAll(ExtensionContext context) {
+ CamelTestSupport support = INSTANCE.get();
+ if (support != null && support.isCreateCamelContextPerClass()) {
+ try {
+ support.tearDownCreateCamelContextPerClass();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Use the RouteBuilder or not
+ *
+ * @return <tt>true</tt> then {@link CamelContext} will be auto started, <tt>false</tt> then {@link CamelContext}
+ * will <b>not</b> be auto started (you will have to start it manually)
+ */
+ public boolean isUseRouteBuilder() {
+ return useRouteBuilder;
+ }
+
+ public void setUseRouteBuilder(boolean useRouteBuilder) {
+ this.useRouteBuilder = useRouteBuilder;
+ }
+
+ /**
+ * Whether to dump route coverage stats at the end of the test.
+ * <p/>
+ * This allows tooling or manual inspection of the stats, so you can generate a route trace diagram of which EIPs
+ * have been in use and which have not. Similar concepts as a code coverage report.
+ * <p/>
+ * You can also turn on route coverage globally via setting JVM system property
+ * <tt>CamelTestRouteCoverage=true</tt>.
+ *
+ * @return <tt>true</tt> to write route coverage status in an xml file in the <tt>target/camel-route-coverage</tt>
+ * directory after the test has finished.
+ */
+ public boolean isDumpRouteCoverage() {
+ return false;
+ }
+
+ /**
+ * Override when using <a href="http://camel.apache.org/advicewith.html">advice with</a> and return <tt>true</tt>.
+ * This helps knowing advice with is to be used, and {@link CamelContext} will not be started before the advice with
+ * takes place. This helps by ensuring the advice with has been property setup before the {@link CamelContext} is
+ * started
+ * <p/>
+ * <b>Important:</b> Its important to start {@link CamelContext} manually from the unit test after you are done
+ * doing all the advice with.
+ *
+ * @return <tt>true</tt> if you use advice with in your unit tests.
+ */
+ public boolean isUseAdviceWith() {
+ return false;
+ }
+
+ /**
+ * Tells whether {@link CamelContext} should be setup per test or per class.
+ * <p/>
+ * By default it will be setup/teardown per test method. This method returns <code>true</code> when the camel test
+ * class is annotated with @TestInstance(TestInstance.Lifecycle.PER_CLASS).
+ * <p/>
+ * <b>Important:</b> Use this with care as the {@link CamelContext} will carry over state from previous tests, such
+ * as endpoints, components etc. So you cannot use this in all your tests.
+ * <p/>
+ * Setting up {@link CamelContext} uses the {@link #doPreSetup()}, {@link #doSetUp()}, and {@link #doPostSetup()}
+ * methods in that given order.
+ *
+ * @return <tt>true</tt> per class, <tt>false</tt> per test.
+ */
+ public final boolean isCreateCamelContextPerClass() {
+ return isCreateCamelContextPerClass;
+ }
+
+ /**
+ * Override to enable auto mocking endpoints based on the pattern.
+ * <p/>
+ * Return <tt>*</tt> to mock all endpoints.
+ *
+ * @see EndpointHelper#matchEndpoint(CamelContext, String, String)
+ */
+ public String isMockEndpoints() {
+ return null;
+ }
+
+ /**
+ * Override to enable auto mocking endpoints based on the pattern, and <b>skip</b> sending to original endpoint.
+ * <p/>
+ * Return <tt>*</tt> to mock all endpoints.
+ *
+ * @see EndpointHelper#matchEndpoint(CamelContext, String, String)
+ */
+ public String isMockEndpointsAndSkip() {
+ return null;
+ }
+
+ public void replaceRouteFromWith(String routeId, String fromEndpoint) {
+ fromEndpoints.put(routeId, fromEndpoint);
+ }
+
+ /**
+ * Used for filtering routes matching the given pattern, which follows the following rules:
+ * <p>
+ * - Match by route id - Match by route input endpoint uri
+ * <p>
+ * The matching is using exact match, by wildcard and regular expression.
+ * <p>
+ * For example to only include routes which starts with foo in their route id's, use: include=foo* And to
+ * exclude routes which starts from JMS endpoints, use: exclude=jms:*
+ * <p>
+ * Multiple patterns can be separated by comma, for example to exclude both foo and bar routes, use:
+ * exclude=foo*,bar*
+ * <p>
+ * Exclude takes precedence over include.
+ */
+ public String getRouteFilterIncludePattern() {
+ return null;
+ }
+
+ /**
+ * Used for filtering routes matching the given pattern, which follows the following rules:
+ * <p>
+ * - Match by route id - Match by route input endpoint uri
+ * <p>
+ * The matching is using exact match, by wildcard and regular expression.
+ * <p>
+ * For example to only include routes which starts with foo in their route id's, use: include=foo* And to
+ * exclude routes which starts from JMS endpoints, use: exclude=jms:*
+ * <p>
+ * Multiple patterns can be separated by comma, for example to exclude both foo and bar routes, use:
+ * exclude=foo*,bar*
+ * <p>
+ * Exclude takes precedence over include.
+ */
+ public String getRouteFilterExcludePattern() {
+ return null;
+ }
+
+ /**
+ * Gets the name of the current test being executed.
+ */
+ public final String getCurrentTestName() {
+ return currentTestName;
+ }
+
+ /**
+ * Override to enable debugger
+ * <p/>
+ * Is default <tt>false</tt>
+ */
+ public boolean isUseDebugger() {
+ return false;
+ }
+
+ public Service getCamelContextService() {
+ return camelContextService;
+ }
+
+ public Service camelContextService() {
+ return camelContextService;
+ }
+
+ public CamelContext context() {
+ return context;
+ }
+
+ public ProducerTemplate template() {
+ return template;
+ }
+
+ public FluentProducerTemplate fluentTemplate() {
+ return fluentTemplate;
+ }
+
+ public ConsumerTemplate consumer() {
+ return consumer;
+ }
+
+ /**
+ * Allows a service to be registered a separate lifecycle service to start and stop the context; such as for Spring
+ * when the ApplicationContext is started and stopped, rather than directly stopping the CamelContext
+ */
+ public void setCamelContextService(Service service) {
+ camelContextService = service;
+ threadService.set(camelContextService);
+ }
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ LOG.info("********************************************************************************");
+ LOG.info("Testing: {} ({})", currentTestName, getClass().getName());
+ LOG.info("********************************************************************************");
+
+ if (isCreateCamelContextPerClass()) {
+ INSTANCE.set(this);
+ AtomicInteger v = TESTS.get();
+ if (v == null) {
+ v = new AtomicInteger();
+ TESTS.set(v);
+ }
+ if (v.getAndIncrement() == 0) {
+ LOG.debug("Setup CamelContext before running first test");
+ // test is per class, so only setup once (the first time)
+ doSpringBootCheck();
+ setupResources();
+ doPreSetup();
+ doSetUp();
+ doPostSetup();
+ } else {
+ LOG.debug("Reset between test methods");
+ // and in between tests we must do IoC and reset mocks
+ postProcessTest();
+ resetMocks();
+ }
+ } else {
+ // test is per test so always setup
+ doSpringBootCheck();
+ setupResources();
+ doPreSetup();
+ doSetUp();
+ doPostSetup();
+ }
+
+ // only start timing after all the setup
+ watch.restart();
+ }
+
+ /**
+ * Strategy to perform any pre setup, before {@link CamelContext} is created
+ */
+ protected void doPreSetup() throws Exception {
+ // noop
+ }
+
+ /**
+ * Strategy to perform any post setup after {@link CamelContext} is created
+ */
+ protected void doPostSetup() throws Exception {
+ // noop
+ }
+
+ /**
+ * Detects if this is a Spring-Boot test and throws an exception, as these base classes is not intended for testing
+ * Camel on Spring Boot.
+ */
+ protected void doSpringBootCheck() {
+ boolean springBoot = hasClassAnnotation("org.springframework.boot.test.context.SpringBootTest");
+ if (springBoot) {
+ throw new RuntimeException(
+ "Spring Boot detected: The CamelTestSupport/CamelSpringTestSupport class is not intended for Camel testing with Spring Boot.");
+ }
+ }
+
+ private void doSetUp() throws Exception {
+ LOG.debug("setUp test");
+ // jmx is enabled if we have configured to use it, or if dump route
+ // coverage is enabled (it requires JMX)
+ boolean jmx = useJmx() || isRouteCoverageEnabled();
+ if (jmx) {
+ enableJMX();
+ } else {
+ disableJMX();
+ }
+
+ context = (ModelCamelContext) createCamelContext();
+ threadCamelContext.set(context);
+
+ assertNotNull(context, "No context found!");
+
+ // add custom beans
+ bindToRegistry(context.getRegistry());
+
+ // reduce default shutdown timeout to avoid waiting for 300 seconds
+ context.getShutdownStrategy().setTimeout(getShutdownTimeout());
+
+ // set debugger if enabled
+ if (isUseDebugger()) {
+ if (context.getStatus().equals(ServiceStatus.Started)) {
+ LOG.info("Cannot setting the Debugger to the starting CamelContext, stop the CamelContext now.");
+ // we need to stop the context first to setup the debugger
+ context.stop();
+ }
+ context.setDebugger(new DefaultDebugger());
+ context.getDebugger().addBreakpoint(breakpoint);
+ // when stopping CamelContext it will automatic remove the
+ // breakpoint
+ }
+
+ template = context.createProducerTemplate();
+ template.start();
+ fluentTemplate = context.createFluentProducerTemplate();
+ fluentTemplate.start();
+ consumer = context.createConsumerTemplate();
+ consumer.start();
+
+ threadTemplate.set(template);
+ threadFluentTemplate.set(fluentTemplate);
+ threadConsumer.set(consumer);
+
+ // enable auto mocking if enabled
+ String pattern = isMockEndpoints();
+ if (pattern != null) {
+ context.adapt(ExtendedCamelContext.class)
+ .registerEndpointCallback(new InterceptSendToMockEndpointStrategy(pattern));
+ }
+ pattern = isMockEndpointsAndSkip();
+ if (pattern != null) {
+ context.adapt(ExtendedCamelContext.class)
+ .registerEndpointCallback(new InterceptSendToMockEndpointStrategy(pattern, true));
+ }
+
+ // configure properties component (mandatory for testing)
+ PropertiesComponent pc = context.getPropertiesComponent();
+ if (extra == null) {
+ extra = useOverridePropertiesWithPropertiesComponent();
+ }
+ if (extra != null && !extra.isEmpty()) {
+ pc.setOverrideProperties(extra);
+ }
+ Boolean ignore = ignoreMissingLocationWithPropertiesComponent();
+ if (ignore != null) {
+ pc.setIgnoreMissingLocation(ignore);
+ }
+
+ String include = getRouteFilterIncludePattern();
+ String exclude = getRouteFilterExcludePattern();
+ if (include != null || exclude != null) {
+ LOG.info("Route filtering pattern: include={}, exclude={}", include, exclude);
+ context.getExtension(Model.class).setRouteFilterPattern(include, exclude);
+ }
+
+ // prepare for in-between tests
+ postProcessTest();
+
+ if (isUseRouteBuilder()) {
+ RoutesBuilder[] builders = createRouteBuilders();
+ for (RoutesBuilder builder : builders) {
+ LOG.debug("Using created route builder: {}", builder);
+ context.addRoutes(builder);
+ }
+ replaceFromEndpoints();
+ boolean skip = "true".equalsIgnoreCase(System.getProperty("skipStartingCamelContext"));
+ if (skip) {
+ LOG.info("Skipping starting CamelContext as system property skipStartingCamelContext is set to be true.");
+ } else if (isUseAdviceWith()) {
+ LOG.info("Skipping starting CamelContext as isUseAdviceWith is set to true.");
+ } else {
+ startCamelContext();
+ }
+ } else {
+ replaceFromEndpoints();
+ LOG.debug("Using route builder from the created context: {}", context);
+ }
+ LOG.debug("Routing Rules are: {}", context.getRoutes());
+
+ assertValidContext(context);
+ }
+
+ private void replaceFromEndpoints() throws Exception {
+ for (final Map.Entry<String, String> entry : fromEndpoints.entrySet()) {
+ AdviceWith.adviceWith(context.getRouteDefinition(entry.getKey()), context, new AdviceWithRouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ replaceFromWith(entry.getValue());
+ }
+ });
+ }
+ }
+
+ private boolean isRouteCoverageEnabled() {
+ return System.getProperty(ROUTE_COVERAGE_ENABLED, "false").equalsIgnoreCase("true") || isDumpRouteCoverage();
+ }
+
+ @AfterEach
+ public void tearDown() throws Exception {
+ long time = watch.taken();
+
+ LOG.info("********************************************************************************");
+ LOG.info("Testing done: {} ({})", currentTestName, getClass().getName());
+ LOG.info("Took: {} ({} millis)", TimeUtils.printDuration(time), time);
+
+ // if we should dump route stats, then write that to a file
+ if (isRouteCoverageEnabled()) {
+ String className = this.getClass().getSimpleName();
+ String dir = "target/camel-route-coverage";
+ String name = className + "-" + StringHelper.before(currentTestName, "(") + ".xml";
+
+ ManagedCamelContext mc = context != null ? context.getExtension(ManagedCamelContext.class) : null;
+ ManagedCamelContextMBean managedCamelContext = mc != null ? mc.getManagedCamelContext() : null;
+ if (managedCamelContext == null) {
+ LOG.warn("Cannot dump route coverage to file as JMX is not enabled. "
+ + "Add camel-management JAR as dependency and/or override useJmx() method to enable JMX in the unit test classes.");
+ } else {
+ routeCoverageDumper.dump(managedCamelContext, context, dir, name, getClass().getName(), currentTestName,
+ timeTaken());
+ }
+ }
+ LOG.info("********************************************************************************");
+
+ if (isCreateCamelContextPerClass()) {
+ // will tear down test specially in afterAll callback
+ } else {
+ LOG.debug("tearDown()");
+ doStopTemplates(consumer, template, fluentTemplate);
+ doStopCamelContext(context, camelContextService);
+ doPostTearDown();
+ cleanupResources();
+ }
+ }
+
+ void tearDownCreateCamelContextPerClass() throws Exception {
+ LOG.debug("tearDownCreateCamelContextPerClass()");
+ TESTS.remove();
+ doStopTemplates(threadConsumer.get(), threadTemplate.get(), threadFluentTemplate.get());
+ doStopCamelContext(threadCamelContext.get(), threadService.get());
+ doPostTearDown();
+ cleanupResources();
+ }
+
+ /**
+ * Strategy to perform any post action, after {@link CamelContext} is stopped
+ */
+ protected void doPostTearDown() throws Exception {
+ // noop
+ }
+
+ /**
+ * Strategy to perform resources setup, before {@link CamelContext} is created
+ */
+ protected void setupResources() throws Exception {
+ // noop
+ }
+
+ /**
+ * Strategy to perform resources cleanup, after {@link CamelContext} is stopped
+ */
+ protected void cleanupResources() throws Exception {
+ // noop
+ }
+
+ /**
+ * Returns the timeout to use when shutting down (unit in seconds).
+ * <p/>
+ * Will default use 10 seconds.
+ *
+ * @return the timeout to use
+ */
+ protected int getShutdownTimeout() {
+ return 10;
+ }
+
+ /**
+ * Whether or not JMX should be used during testing.
+ *
+ * @return <tt>false</tt> by default.
+ */
+ protected boolean useJmx() {
+ return false;
+ }
+
+ /**
+ * Override this method to include and override properties with the Camel {@link PropertiesComponent}.
+ *
+ * @return additional properties to add/override.
+ */
+ protected Properties useOverridePropertiesWithPropertiesComponent() {
+ return null;
+ }
+
+ /**
+ * Whether to ignore missing locations with the {@link PropertiesComponent}. For example when unit testing you may
+ * want to ignore locations that are not available in the environment used for testing.
+ *
+ * @return <tt>true</tt> to ignore, <tt>false</tt> to not ignore, and <tt>null</tt> to leave as configured on the
+ * {@link PropertiesComponent}
+ */
+ protected Boolean ignoreMissingLocationWithPropertiesComponent() {
+ return null;
+ }
+
+ protected void postProcessTest() throws Exception {
+ context = threadCamelContext.get();
+ template = threadTemplate.get();
+ fluentTemplate = threadFluentTemplate.get();
+ consumer = threadConsumer.get();
+ camelContextService = threadService.get();
+ applyCamelPostProcessor();
+ }
+
+ /**
+ * Applies the {@link CamelBeanPostProcessor} to this instance.
+ * <p>
+ * Derived classes using IoC / DI frameworks may wish to turn this into a NoOp such as for CDI we would just use CDI
+ * to inject this
+ */
+ protected void applyCamelPostProcessor() throws Exception {
+ // use the bean post processor if the test class is not dependency
+ // injected already by Spring Framework
+ boolean spring = hasClassAnnotation("org.springframework.boot.test.context.SpringBootTest",
+ "org.springframework.context.annotation.ComponentScan");
+ if (!spring) {
+ context.getExtension(ExtendedCamelContext.class).getBeanPostProcessor().postProcessBeforeInitialization(this,
+ getClass().getName());
+ context.getExtension(ExtendedCamelContext.class).getBeanPostProcessor().postProcessAfterInitialization(this,
+ getClass().getName());
+ }
+ }
+
+ /**
+ * Does this test class have any of the following annotations on the class-level.
+ */
+ protected boolean hasClassAnnotation(String... names) {
+ for (String name : names) {
+ for (Annotation ann : getClass().getAnnotations()) {
+ String annName = ann.annotationType().getName();
+ if (annName.equals(name)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ protected void stopCamelContext() throws Exception {
+ doStopCamelContext(context, camelContextService);
+ }
+
+ private static void doStopCamelContext(CamelContext context, Service camelContextService) {
+ if (camelContextService != null) {
+ if (camelContextService == threadService.get()) {
+ threadService.remove();
+ }
+ camelContextService.stop();
+ } else {
+ if (context != null) {
+ if (context == threadCamelContext.get()) {
+ threadCamelContext.remove();
+ }
+ context.stop();
+ }
+ }
+ }
+
+ private static void doStopTemplates(
+ ConsumerTemplate consumer, ProducerTemplate template, FluentProducerTemplate fluentTemplate) {
+ if (consumer != null) {
+ if (consumer == threadConsumer.get()) {
+ threadConsumer.remove();
+ }
+ consumer.stop();
+ }
+ if (template != null) {
+ if (template == threadTemplate.get()) {
+ threadTemplate.remove();
+ }
+ template.stop();
+ }
+ if (fluentTemplate != null) {
+ if (fluentTemplate == threadFluentTemplate.get()) {
+ threadFluentTemplate.remove();
+ }
+ fluentTemplate.stop();
+ }
+ }
+
+ protected void startCamelContext() throws Exception {
+ if (camelContextService != null) {
+ camelContextService.start();
+ } else {
+ if (context instanceof DefaultCamelContext) {
+ DefaultCamelContext defaultCamelContext = (DefaultCamelContext) context;
+ if (!defaultCamelContext.isStarted()) {
+ defaultCamelContext.start();
+ }
+ } else {
+ context.start();
+ }
+ }
+ }
+
+ protected CamelContext createCamelContext() throws Exception {
+ Registry registry = createCamelRegistry();
+
+ CamelContext retContext;
+ if (registry != null) {
+ retContext = new DefaultCamelContext(registry);
+ } else {
+ retContext = new DefaultCamelContext();
+ }
+
+ return retContext;
+ }
+
+ /**
+ * Allows to bind custom beans to the Camel {@link Registry}.
+ */
+ protected void bindToRegistry(Registry registry) throws Exception {
+ // noop
+ }
+
+ /**
+ * Override to use a custom {@link Registry}.
+ * <p>
+ * However if you need to bind beans to the registry then this is possible already with the bind method on registry,
+ * and there is no need to override this method.
+ */
+ protected Registry createCamelRegistry() throws Exception {
+ return null;
+ }
+
+ /**
+ * Factory method which derived classes can use to create a {@link RouteBuilder} to define the routes for testing
+ */
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ // no routes added by default
+ }
+ };
+ }
+
+ /**
+ * Factory method which derived classes can use to create an array of {@link org.apache.camel.builder.RouteBuilder}s
+ * to define the routes for testing
+ *
+ * @see #createRouteBuilder()
+ */
+ protected RoutesBuilder[] createRouteBuilders() throws Exception {
+ return new RoutesBuilder[] { createRouteBuilder() };
+ }
+
+ /**
+ * Resolves a mandatory endpoint for the given URI or an exception is thrown
+ *
+ * @param uri the Camel <a href="">URI</a> to use to create or resolve an endpoint
+ * @return the endpoint
+ */
+ protected Endpoint resolveMandatoryEndpoint(String uri) {
+ return TestSupport.resolveMandatoryEndpoint(context, uri);
+ }
+
+ /**
+ * Resolves a mandatory endpoint for the given URI and expected type or an exception is thrown
+ *
+ * @param uri the Camel <a href="">URI</a> to use to create or resolve an endpoint
+ * @return the endpoint
+ */
+ protected <T extends Endpoint> T resolveMandatoryEndpoint(String uri, Class<T> endpointType) {
+ return TestSupport.resolveMandatoryEndpoint(context, uri, endpointType);
+ }
+
+ /**
+ * Resolves the mandatory Mock endpoint using a URI of the form <code>mock:someName</code>
+ *
+ * @param uri the URI which typically starts with "mock:" and has some name
+ * @return the mandatory mock endpoint or an exception is thrown if it could not be resolved
+ */
+ protected MockEndpoint getMockEndpoint(String uri) {
+ return getMockEndpoint(uri, true);
+ }
+
+ /**
+ * Resolves the {@link MockEndpoint} using a URI of the form <code>mock:someName</code>, optionally creating it if
+ * it does not exist. This implementation will lookup existing mock endpoints and match on the mock queue name, eg
+ * mock:foo and mock:foo?retainFirst=5 would match as the queue name is foo.
+ *
+ * @param uri the URI which typically starts with "mock:" and has some name
+ * @param create whether or not to allow the endpoint to be created if it doesn't exist
+ * @return the mock endpoint or an {@link NoSuchEndpointException} is thrown if it could not
+ * be resolved
+ * @throws NoSuchEndpointException is the mock endpoint does not exists
+ */
+ protected MockEndpoint getMockEndpoint(String uri, boolean create) throws NoSuchEndpointException {
+ // look for existing mock endpoints that have the same queue name, and
+ // to
+ // do that we need to normalize uri and strip out query parameters and
+ // whatnot
+ String n;
+ try {
+ n = URISupport.normalizeUri(uri);
+ } catch (Exception e) {
+ throw RuntimeCamelException.wrapRuntimeException(e);
+ }
+ // strip query
+ int idx = n.indexOf('?');
+ if (idx != -1) {
+ n = n.substring(0, idx);
+ }
+ final String target = n;
+
+ // lookup endpoints in registry and try to find it
+ MockEndpoint found = (MockEndpoint) context.getEndpointRegistry().values().stream()
+ .filter(e -> e instanceof MockEndpoint).filter(e -> {
+ String t = e.getEndpointUri();
+ // strip query
+ int idx2 = t.indexOf('?');
+ if (idx2 != -1) {
+ t = t.substring(0, idx2);
+ }
+ return t.equals(target);
+ }).findFirst().orElse(null);
+
+ if (found != null) {
+ return found;
+ }
+
+ if (create) {
+ return resolveMandatoryEndpoint(uri, MockEndpoint.class);
+ } else {
+ throw new NoSuchEndpointException(String.format("MockEndpoint %s does not exist.", uri));
+ }
+ }
+
+ /**
+ * Sends a message to the given endpoint URI with the body value
+ *
+ * @param endpointUri the URI of the endpoint to send to
+ * @param body the body for the message
+ */
+ protected void sendBody(String endpointUri, final Object body) {
+ template.send(endpointUri, exchange -> {
+ Message in = exchange.getIn();
+ in.setBody(body);
+ });
+ }
+
+ /**
+ * Sends a message to the given endpoint URI with the body value and specified headers
+ *
+ * @param endpointUri the URI of the endpoint to send to
+ * @param body the body for the message
+ * @param headers any headers to set on the message
+ */
+ protected void sendBody(String endpointUri, final Object body, final Map<String, Object> headers) {
+ template.send(endpointUri, exchange -> {
+ Message in = exchange.getIn();
+ in.setBody(body);
+ for (Map.Entry<String, Object> entry : headers.entrySet()) {
+ in.setHeader(entry.getKey(), entry.getValue());
+ }
+ });
+ }
+
+ /**
+ * Sends messages to the given endpoint for each of the specified bodies
+ *
+ * @param endpointUri the endpoint URI to send to
+ * @param bodies the bodies to send, one per message
+ */
+ protected void sendBodies(String endpointUri, Object... bodies) {
+ for (Object body : bodies) {
+ sendBody(endpointUri, body);
+ }
+ }
+
+ /**
+ * Creates an exchange with the given body
+ */
+ protected Exchange createExchangeWithBody(Object body) {
+ return TestSupport.createExchangeWithBody(context, body);
+ }
+
+ /**
+ * Asserts that the given language name and expression evaluates to the given value on a specific exchange
+ */
+ protected void assertExpression(Exchange exchange, String languageName, String expressionText, Object expectedValue) {
+ Language language = assertResolveLanguage(languageName);
+
+ Expression expression = language.createExpression(expressionText);
+ assertNotNull(expression, "No Expression could be created for text: " + expressionText + " language: " + language);
+
+ TestSupport.assertExpression(expression, exchange, expectedValue);
+ }
+
+ /**
+ * Asserts that the given language name and predicate expression evaluates to the expected value on the message
+ * exchange
+ */
+ protected void assertPredicate(String languageName, String expressionText, Exchange exchange, boolean expected) {
+ Language language = assertResolveLanguage(languageName);
+
+ Predicate predicate = language.createPredicate(expressionText);
+ assertNotNull(predicate, "No Predicate could be created for text: " + expressionText + " language: " + language);
+
+ TestSupport.assertPredicate(predicate, exchange, expected);
+ }
+
+ /**
+ * Asserts that the language name can be resolved
+ */
+ protected Language assertResolveLanguage(String languageName) {
+ Language language = context.resolveLanguage(languageName);
+ assertNotNull(language, "No language found for name: " + languageName);
+ return language;
+ }
+
+ /**
+ * Asserts that all the expectations of the Mock endpoints are valid
+ */
+ protected void assertMockEndpointsSatisfied() throws InterruptedException {
+ MockEndpoint.assertIsSatisfied(context);
+ }
+
+ /**
+ * Asserts that all the expectations of the Mock endpoints are valid
+ */
+ protected void assertMockEndpointsSatisfied(long timeout, TimeUnit unit) throws InterruptedException {
+ MockEndpoint.assertIsSatisfied(context, timeout, unit);
+ }
+
+ /**
+ * Reset all Mock endpoints.
+ */
+ protected void resetMocks() {
+ MockEndpoint.resetMocks(context);
+ }
+
+ protected void assertValidContext(CamelContext context) {
+ assertNotNull(context, "No context found!");
+ }
+
+ protected <T extends Endpoint> T getMandatoryEndpoint(String uri, Class<T> type) {
+ T endpoint = context.getEndpoint(uri, type);
+ assertNotNull(endpoint, "No endpoint found for uri: " + uri);
+ return endpoint;
+ }
+
+ protected Endpoint getMandatoryEndpoint(String uri) {
+ Endpoint endpoint = context.getEndpoint(uri);
+ assertNotNull(endpoint, "No endpoint found for uri: " + uri);
+ return endpoint;
+ }
+
+ /**
+ * Disables the JMX agent. Must be called before the {@link #setUp()} method.
+ */
+ protected void disableJMX() {
+ DefaultCamelContext.setDisableJmx(true);
+ }
+
+ /**
+ * Enables the JMX agent. Must be called before the {@link #setUp()} method.
+ */
+ protected void enableJMX() {
+ DefaultCamelContext.setDisableJmx(false);
+ }
+
+ /**
+ * Single step debugs and Camel invokes this method before entering the given processor
+ */
+ protected void debugBefore(
+ Exchange exchange, Processor processor, ProcessorDefinition<?> definition, String id, String label) {
+ }
+
+ /**
+ * Single step debugs and Camel invokes this method after processing the given processor
+ */
+ protected void debugAfter(
+ Exchange exchange, Processor processor, ProcessorDefinition<?> definition, String id, String label,
+ long timeTaken) {
+ }
+
+ /**
+ * To easily debug by overriding the <tt>debugBefore</tt> and <tt>debugAfter</tt> methods.
+ */
+ private class DebugBreakpoint extends BreakpointSupport {
+
+ @Override
+ public void beforeProcess(Exchange exchange, Processor processor, NamedNode definition) {
+ CamelTestSupport.this.debugBefore(exchange, processor, (ProcessorDefinition<?>) definition, definition.getId(),
+ definition.getLabel());
+ }
+
+ @Override
+ public void afterProcess(Exchange exchange, Processor processor, NamedNode definition, long timeTaken) {
+ CamelTestSupport.this.debugAfter(exchange, processor, (ProcessorDefinition<?>) definition, definition.getId(),
+ definition.getLabel(), timeTaken);
+ }
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/EndpointUriFactoryTestSupport.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/EndpointUriFactoryTestSupport.java
new file mode 100644
index 0000000..ffc0172
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/EndpointUriFactoryTestSupport.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5;
+
+import java.util.Map;
+
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.spi.EndpointUriFactory;
+import org.junit.jupiter.api.Test;
+
+public abstract class EndpointUriFactoryTestSupport extends CamelTestSupport {
+
+ @Test
+ public void buildUri() {
+ // let's just see if the route starts successfully
+ }
+
+ protected abstract String scheme();
+
+ protected abstract Map<String, Object> pathParameters();
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() throws Exception {
+ EndpointUriFactory factory = getContext().adapt(ExtendedCamelContext.class).getEndpointUriFactory(scheme());
+ String uri = factory.buildUri(scheme(), pathParameters(), false);
+ from(uri).to("mock:out");
+ }
+ };
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/ExchangeTestSupport.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/ExchangeTestSupport.java
new file mode 100644
index 0000000..14727c1
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/ExchangeTestSupport.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.support.DefaultExchange;
+import org.junit.jupiter.api.BeforeEach;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * A base class for a test which requires a {@link org.apache.camel.CamelContext} and a populated {@link Exchange}
+ */
+public abstract class ExchangeTestSupport extends CamelTestSupport {
+ protected Exchange exchange;
+
+ /**
+ * A factory method to create an Exchange implementation
+ */
+ protected Exchange createExchange() {
+ return new DefaultExchange(context);
+ }
+
+ /**
+ * A strategy method to populate an exchange with some example values for use by language plugins
+ */
+ protected void populateExchange(Exchange exchange) {
+ Message in = exchange.getIn();
+ in.setHeader("foo", "abc");
+ in.setHeader("bar", 123);
+ in.setBody("<hello id='m123'>world!</hello>");
+ }
+
+ @Override
+ @BeforeEach
+ public void setUp() throws Exception {
+ super.setUp();
+ exchange = createExchange();
+ assertNotNull(exchange, "No exchange created!");
+ populateExchange(exchange);
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/LanguageTestSupport.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/LanguageTestSupport.java
new file mode 100644
index 0000000..c2f2241
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/LanguageTestSupport.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.spi.Language;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * A useful base class for testing the language plugins in Camel
+ */
+public abstract class LanguageTestSupport extends ExchangeTestSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(LanguageTestSupport.class);
+
+ protected abstract String getLanguageName();
+
+ /**
+ * Asserts that the given predicate expression evaluated on the current language and message exchange evaluates to
+ * true
+ */
+ protected void assertPredicate(String expression) {
+ assertPredicate(exchange, expression, true);
+ }
+
+ /**
+ * Asserts that the given predicate expression evaluated on the current language and message exchange evaluates to
+ * false
+ */
+ protected void assertPredicateFails(String expression) {
+ assertPredicate(exchange, expression, false);
+ }
+
+ /**
+ * Asserts that the given predicate expression evaluated on the current language and message exchange evaluates to
+ * the expected value
+ */
+ protected void assertPredicate(String expression, boolean expected) {
+ assertPredicate(exchange, expression, expected);
+ }
+
+ protected void assertPredicate(Exchange exchange, String expression, boolean expected) {
+ assertPredicate(getLanguageName(), expression, exchange, expected);
+ }
+
+ /**
+ * Asserts that this language expression evaluates to the given value on the given exchange
+ */
+ protected void assertExpression(Exchange exchange, String expressionText, Object expectedValue) {
+ assertExpression(exchange, getLanguageName(), expressionText, expectedValue);
+ }
+
+ /**
+ * Asserts that this language expression evaluates to the given value on the current exchange
+ */
+ protected void assertExpression(String expressionText, Object expectedValue) {
+ assertExpression(exchange, expressionText, expectedValue);
+ }
+
+ /**
+ * Asserts that the expression evaluates to one of the two given values
+ */
+ protected void assertExpression(String expressionText, String expectedValue, String orThisExpectedValue) {
+ Language language = assertResolveLanguage(getLanguageName());
+
+ Expression expression = language.createExpression(expressionText);
+ assertNotNull(expression, "No Expression could be created for text: " + expressionText + " language: " + language);
+
+ Object value;
+ if (expectedValue != null) {
+ value = expression.evaluate(exchange, expectedValue.getClass());
+ } else {
+ value = expression.evaluate(exchange, Object.class);
+ }
+ LOG.debug("Evaluated expression: {} on exchange: {} result: {}", expression, exchange, value);
+
+ assertTrue(expectedValue.equals(value) || orThisExpectedValue.equals(value),
+ "Expression: " + expression + " on Exchange: " + exchange);
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/TestSupport.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/TestSupport.java
new file mode 100644
index 0000000..1670bbb
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/TestSupport.java
@@ -0,0 +1,539 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.Expression;
+import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.Message;
+import org.apache.camel.Predicate;
+import org.apache.camel.Route;
+import org.apache.camel.builder.Builder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.builder.ValueBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.support.PredicateAssertHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Provides utility methods for camel test purpose (builders, assertions, endpoint resolutions, file helpers).
+ */
+public final class TestSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TestSupport.class);
+
+ private TestSupport() {
+ }
+
+ // -------------------------------------------------------------------------
+ // Builder methods for expressions used when testing
+ // -------------------------------------------------------------------------
+
+ /**
+ * Returns a value builder for the given header.
+ */
+ public static ValueBuilder header(String name) {
+ return Builder.header(name);
+ }
+
+ /**
+ * Returns a value builder for the given exchange property.
+ */
+ public static ValueBuilder exchangeProperty(String name) {
+ return Builder.exchangeProperty(name);
+ }
+
+ /**
+ * Returns a predicate and value builder for the inbound body on an exchange.
+ */
+ public static ValueBuilder body() {
+ return Builder.body();
+ }
+
+ /**
+ * Returns a predicate and value builder for the inbound message body as a specific type.
+ */
+ public static <T> ValueBuilder bodyAs(Class<T> type) {
+ return Builder.bodyAs(type);
+ }
+
+ /**
+ * Returns a value builder for the given system property.
+ */
+ public static ValueBuilder systemProperty(String name) {
+ return Builder.systemProperty(name);
+ }
+
+ /**
+ * Returns a value builder for the given system property.
+ */
+ public static ValueBuilder systemProperty(String name, String defaultValue) {
+ return Builder.systemProperty(name, defaultValue);
+ }
+
+ // -----------------------------------------------------------------------
+ // Assertions
+ // -----------------------------------------------------------------------
+
+ /**
+ * Asserts that a given value is of an expected type.
+ */
+ public static <T> T assertIsInstanceOf(Class<T> expectedType, Object value) {
+ assertNotNull(value, "Expected an instance of type: " + expectedType.getName() + " but was null");
+ assertTrue(expectedType.isInstance(value), "Object should be of type " + expectedType.getName() + " but was: " + value
+ + " with the type: " + value.getClass().getName());
+ return expectedType.cast(value);
+ }
+
+ /**
+ * Asserts that a given endpoint has an expected uri.
+ */
+ public static void assertEndpointUri(Endpoint endpoint, String expectedUri) {
+ assertNotNull(endpoint, "Endpoint is null when expecting endpoint for: " + expectedUri);
+ assertEquals(expectedUri, endpoint.getEndpointUri(), "Endpoint uri for: " + endpoint);
+ }
+
+ /**
+ * Asserts that the In message on the exchange contains an header with a given name and expected value.
+ */
+ public static Object assertInMessageHeader(Exchange exchange, String headerName, Object expectedValue) {
+ return assertMessageHeader(exchange.getIn(), headerName, expectedValue);
+ }
+
+ /**
+ * Asserts that the message on the exchange contains an header with a given name and expected value.
+ */
+ public static Object assertOutMessageHeader(Exchange exchange, String headerName, Object expectedValue) {
+ return assertMessageHeader(exchange.getMessage(), headerName, expectedValue);
+ }
+
+ /**
+ * Asserts that the given exchange has a given expectedBody on the IN message.
+ */
+ public static void assertInMessageBodyEquals(Exchange exchange, Object expectedBody) throws InvalidPayloadException {
+ assertNotNull(exchange, "Should have a response exchange");
+
+ Object actualBody;
+ if (expectedBody == null) {
+ actualBody = exchange.getIn().getMandatoryBody();
+ assertEquals(expectedBody, actualBody, "in body of: " + exchange);
+ } else {
+ actualBody = exchange.getIn().getMandatoryBody(expectedBody.getClass());
+ }
+ assertEquals(expectedBody, actualBody, "in body of: " + exchange);
+
+ LOG.debug("Received response: {} with in: {}", exchange, exchange.getIn());
+ }
+
+ /**
+ * Asserts that the given exchange has a given expectedBody on the message.
+ */
+ public static void assertMessageBodyEquals(Exchange exchange, Object expectedBody) throws InvalidPayloadException {
+ assertNotNull(exchange, "Should have a response exchange!");
+
+ Object actualBody;
+ if (expectedBody == null) {
+ actualBody = exchange.getMessage().getMandatoryBody();
+ assertEquals(expectedBody, actualBody, "output body of: " + exchange);
+ } else {
+ actualBody = exchange.getMessage().getMandatoryBody(expectedBody.getClass());
+ }
+ assertEquals(expectedBody, actualBody, "output body of: " + exchange);
+
+ LOG.debug("Received response: {} with out: {}", exchange, exchange.getMessage());
+ }
+
+ /**
+ * Asserts that a given message contains an header with a given name and expected value.
+ */
+ public static Object assertMessageHeader(Message message, String headerName, Object expectedValue) {
+ Object actualValue = message.getHeader(headerName);
+ assertEquals(expectedValue, actualValue, "Header: " + headerName + " on Message: " + message);
+ return actualValue;
+ }
+
+ /**
+ * Asserts that the given expression when evaluated returns the given answer.
+ */
+ public static Object assertExpression(Expression expression, Exchange exchange, Object expectedAnswer) {
+ Object actualAnswer;
+ if (expectedAnswer != null) {
+ actualAnswer = expression.evaluate(exchange, expectedAnswer.getClass());
+ } else {
+ actualAnswer = expression.evaluate(exchange, Object.class);
+ }
+
+ LOG.debug("Evaluated expression: {} on exchange: {} result: {}", expression, exchange, actualAnswer);
+
+ assertEquals(expectedAnswer, actualAnswer, "Expression: " + expression + " on Exchange: " + exchange);
+ return actualAnswer;
+ }
+
+ /**
+ * Asserts that a given predicate returns <code>true</code> on a given exchange.
+ */
+ public static void assertPredicateMatches(Predicate predicate, Exchange exchange) {
+ assertPredicate(predicate, exchange, true);
+ }
+
+ /**
+ * Asserts that a given predicate returns <code>false</code> on a given exchange.
+ */
+ public static void assertPredicateDoesNotMatch(Predicate predicate, Exchange exchange) {
+ try {
+ PredicateAssertHelper.assertMatches(predicate, "Predicate should match: ", exchange);
+ } catch (AssertionError e) {
+ LOG.debug("Caught expected assertion error: {}", e.getMessage(), e);
+ }
+ assertPredicate(predicate, exchange, false);
+ }
+
+ /**
+ * Asserts that the predicate returns the expected value on the exchange.
+ */
+ public static boolean assertPredicate(final Predicate predicate, Exchange exchange, boolean expectedValue) {
+ if (expectedValue) {
+ PredicateAssertHelper.assertMatches(predicate, "Predicate failed: ", exchange);
+ }
+ boolean actualValue = predicate.matches(exchange);
+
+ LOG.debug("Evaluated predicate: {} on exchange: {} result: {}", predicate, exchange, actualValue);
+
+ assertEquals(expectedValue, actualValue, "Predicate: " + predicate + " on Exchange: " + exchange);
+ return actualValue;
+ }
+
+ /**
+ * Asserts that a given list has a single element.
+ */
+ public static <T> T assertOneElement(List<T> list) {
+ assertEquals(1, list.size(), "Size of list should be 1: " + list);
+ return list.get(0);
+ }
+
+ /**
+ * Asserts that a given list has a given expected size.
+ */
+ public static <T> List<T> assertListSize(List<T> list, int expectedSize) {
+ return assertListSize("List", list, expectedSize);
+ }
+
+ /**
+ * Asserts that a list is of the given size. When the assertion is broken, the error message starts with a given
+ * prefix.
+ */
+ public static <T> List<T> assertListSize(String brokenAssertionMessagePrefix, List<T> list, int expectedSize) {
+ assertEquals(expectedSize, list.size(),
+ brokenAssertionMessagePrefix + " should be of size: " + expectedSize + " but is: " + list);
+ return list;
+ }
+
+ /**
+ * Asserts that a given collection has a given size.
+ */
+ public static <T> Collection<T> assertCollectionSize(Collection<T> list, int expectedSize) {
+ return assertCollectionSize("List", list, expectedSize);
+ }
+
+ /**
+ * Asserts that a given collection has a given size. When the assertion is broken, the error message starts with a
+ * given prefix.
+ */
+ public static <
+ T> Collection<T> assertCollectionSize(String brokenAssertionMessagePrefix, Collection<T> list, int expectedSize) {
+ assertEquals(expectedSize, list.size(),
+ brokenAssertionMessagePrefix + " should be of size: " + expectedSize + " but is: " + list);
+ return list;
+ }
+
+ /**
+ * Asserts that the text contains the given string.
+ *
+ * @param text the text to compare
+ * @param containedText the text which must be contained inside the other text parameter
+ */
+ public static void assertStringContains(String text, String containedText) {
+ assertNotNull(text, "Text should not be null!");
+ assertTrue(text.contains(containedText), "Text: " + text + " does not contain: " + containedText);
+ }
+
+ /**
+ * Asserts that two given directories are equal. To be used for folder/directory comparison that works across
+ * different platforms such as Window, Mac and Linux.
+ */
+ public static void assertDirectoryEquals(String expected, String actual) {
+ assertDirectoryEquals(null, expected, actual);
+ }
+
+ /**
+ * Asserts that two given directories are equal. To be used for folder/directory comparison that works across
+ * different platforms such as Window, Mac and Linux.
+ */
+ public static void assertDirectoryEquals(String message, String expected, String actual) {
+ // must use single / as path separators
+ String expectedPath = expected.replace('\\', '/');
+ String actualPath = actual.replace('\\', '/');
+
+ if (message != null) {
+ assertEquals(message, expectedPath, actualPath);
+ } else {
+ assertEquals(expectedPath, actualPath);
+ }
+ }
+
+ /**
+ * Asserts that a given directory is found in the file system.
+ */
+ public static void assertDirectoryExists(String filename) {
+ File file = new File(filename);
+ assertTrue(file.exists(), "Directory " + filename + " should exist");
+ assertTrue(file.isDirectory(), "Directory " + filename + " should be a directory");
+ }
+
+ /**
+ * Asserts that a given file is found in the file system.
+ */
+ public static void assertFileExists(String filename) {
+ File file = new File(filename);
+ assertTrue(file.exists(), "File " + filename + " should exist");
+ assertTrue(file.isFile(), "File " + filename + " should be a file");
+ }
+
+ /**
+ * Asserts that a given file is <b>not</b> found in the file system.
+ */
+ public static void assertFileNotExists(String filename) {
+ File file = new File(filename);
+ assertFalse(file.exists(), "File " + filename + " should not exist");
+ }
+
+ // -----------------------------------------------------------------------
+ // Other helpers, resolution, file, getRouteList
+ // -----------------------------------------------------------------------
+
+ /**
+ * Resolves an endpoint and asserts that it is found.
+ */
+ public static Endpoint resolveMandatoryEndpoint(CamelContext context, String endpointUri) {
+ Endpoint endpoint = context.getEndpoint(endpointUri);
+
+ assertNotNull(endpoint, "No endpoint found for URI: " + endpointUri);
+
+ return endpoint;
+ }
+
+ /**
+ * Resolves an endpoint and asserts that it is found.
+ */
+ public static <
+ T extends Endpoint> T resolveMandatoryEndpoint(CamelContext context, String endpointUri, Class<T> endpointType) {
+ T endpoint = context.getEndpoint(endpointUri, endpointType);
+
+ assertNotNull(endpoint, "No endpoint found for URI: " + endpointUri);
+
+ return endpoint;
+ }
+
+ /**
+ * Creates an exchange with the given body.
+ */
+ public static Exchange createExchangeWithBody(CamelContext camelContext, Object body) {
+ Exchange exchange = new DefaultExchange(camelContext);
+ Message message = exchange.getIn();
+ message.setBody(body);
+ return exchange;
+ }
+
+ /**
+ * A helper method to create a list of Route objects for a given route builder.
+ */
+ public static List<Route> getRouteList(RouteBuilder builder) throws Exception {
+ CamelContext context = new DefaultCamelContext();
+ context.addRoutes(builder);
+ context.start();
+ List<Route> answer = context.getRoutes();
+ context.stop();
+ return answer;
+ }
+
+ /**
+ * Recursively delete a directory, useful to zapping test data. Deletion will be attempted up to five time before
+ * giving up.
+ *
+ * @param file the directory to be deleted
+ * @return <tt>false</tt> when an error occur while deleting directory
+ */
+ public static boolean deleteDirectory(String file) {
+ return deleteDirectory(new File(file));
+ }
+
+ /**
+ * Recursively delete a directory, useful to zapping test data. Deletion will be attempted up to five time before
+ * giving up.
+ *
+ * @param file the directory to be deleted
+ * @return <tt>false</tt> when an error occur while deleting directory
+ */
+ public static boolean deleteDirectory(File file) {
+ int tries = 0;
+ int maxTries = 5;
+ boolean exists = true;
+ while (exists && (tries < maxTries)) {
+ recursivelyDeleteDirectory(file);
+ tries++;
+ exists = file.exists();
+ if (exists) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ }
+ return !exists;
+ }
+
+ /**
+ * Recursively delete a directory. Deletion will be attempted a single time before giving up.
+ *
+ * @param file the directory to be deleted
+ */
+ public static void recursivelyDeleteDirectory(File file) {
+ if (!file.exists()) {
+ return;
+ }
+
+ if (file.isDirectory()) {
+ File[] files = file.listFiles();
+ for (File child : files) {
+ recursivelyDeleteDirectory(child);
+ }
+ }
+ boolean success = file.delete();
+ if (!success) {
+ LOG.warn("Deletion of file: {} failed", file.getAbsolutePath());
+ }
+ }
+
+ /**
+ * Creates a given directory.
+ *
+ * @param file the directory to be created
+ */
+ public static void createDirectory(String file) {
+ File dir = new File(file);
+ dir.mkdirs();
+ }
+
+ /**
+ * Tells whether the current Operating System is the given expected platform.
+ * <p/>
+ * Uses <tt>os.name</tt> from the system properties to determine the Operating System.
+ *
+ * @param expectedPlatform such as Windows
+ * @return <tt>true</tt> when the current Operating System is the expected platform, <tt>false</tt>
+ * otherwise.
+ */
+ public static boolean isPlatform(String expectedPlatform) {
+ String osName = System.getProperty("os.name").toLowerCase(Locale.US);
+ return osName.contains(expectedPlatform.toLowerCase(Locale.US));
+ }
+
+ /**
+ * Tells whether the current Java Virtual Machine has been issued by a given expected vendor.
+ * <p/>
+ * Uses <tt>java.vendor</tt> from the system properties to determine the vendor.
+ *
+ * @param expectedVendor such as IBM
+ * @return <tt>true</tt> when the current Java Virtual Machine has been issued by the expected
+ * vendor, <tt>false</tt> otherwise.
+ */
+ public static boolean isJavaVendor(String expectedVendor) {
+ String javaVendor = System.getProperty("java.vendor").toLowerCase(Locale.US);
+ return javaVendor.contains(expectedVendor.toLowerCase(Locale.US));
+ }
+
+ /**
+ * Tells whether the current Java version is 1.8.
+ *
+ * @return <tt>true</tt> if its Java 1.8, <tt>false</tt> if its not (for example Java 1.7 or older)
+ */
+ public static boolean isJava18() {
+ return getJavaMajorVersion() == 8;
+
+ }
+
+ /**
+ * Tells whether the current Java version is 1.8 and build_no 261 and later.
+ *
+ * @return <tt>true</tt> if its Java 1.8.0_261 and later, <tt>false</tt> if its not (for example Java 1.8.0_251)
+ */
+ // CHECKSTYLE:OFF
+ public static boolean isJava18_261_later() {
+ boolean ret;
+ String version = System.getProperty("java.version");
+ try {
+ ret = version != null && version.startsWith("1.8.0_")
+ && Integer.parseInt(version.substring(6)) >= 261;
+ } catch (NumberFormatException ex) {
+ ret = false;
+ }
+ return ret;
+ }
+ // CHECKSTYLE:ON
+
+ /**
+ * Tells whether the current Java version is 1.9.
+ *
+ * @return <tt>true</tt> if its Java 1.9, <tt>false</tt> if its not (for example Java 1.8 or older)
+ */
+ public static boolean isJava19() {
+ return getJavaMajorVersion() == 9;
+
+ }
+
+ /**
+ * Returns the current major Java version e.g 8.
+ * <p/>
+ * Uses <tt>java.specification.version</tt> from the system properties to determine the major version.
+ *
+ * @return the current major Java version.
+ */
+ public static int getJavaMajorVersion() {
+ String javaSpecVersion = System.getProperty("java.specification.version");
+ if (javaSpecVersion.contains(".")) { // before jdk 9
+ return Integer.parseInt(javaSpecVersion.split("\\.")[1]);
+ } else {
+ return Integer.parseInt(javaSpecVersion);
+ }
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/Parameter.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/Parameter.java
new file mode 100644
index 0000000..e5a1fac
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/Parameter.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.params;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Parameter {
+ int value() default 0;
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/Parameterized.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/Parameterized.java
new file mode 100644
index 0000000..048585b
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/Parameterized.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.params;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@ExtendWith(ParameterizedExtension.class)
+public @interface Parameterized {
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/ParameterizedExtension.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/ParameterizedExtension.java
new file mode 100644
index 0000000..ac61a0e
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/ParameterizedExtension.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.params;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.extension.Extension;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.TestInstancePostProcessor;
+import org.junit.jupiter.api.extension.TestInstantiationException;
+import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
+import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;
+import org.junit.jupiter.params.converter.DefaultArgumentConverter;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.platform.commons.util.CollectionUtils;
+import org.junit.platform.commons.util.ReflectionUtils;
+
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+import static org.junit.platform.commons.util.AnnotationUtils.isAnnotated;
+
+public class ParameterizedExtension implements TestTemplateInvocationContextProvider {
+
+ @Override
+ public boolean supportsTestTemplate(ExtensionContext context) {
+ return context.getTestMethod()
+ .map(m -> isAnnotated(m, Test.class))
+ .orElse(false);
+ }
+
+ @Override
+ public java.util.stream.Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
+ ExtensionContext extensionContext) {
+ Class<?> testClass = extensionContext.getRequiredTestClass();
+ try {
+ List<Method> parameters = getParametersMethods(testClass);
+ if (parameters.size() != 1) {
+ throw new IllegalStateException(
+ "Class " + testClass.getName() + " should provide a single method annotated with @"
+ + Parameters.class.getSimpleName());
+ }
+ Object params = parameters.iterator().next().invoke(null);
+ return CollectionUtils.toStream(params)
+ .map(ParameterizedExtension::toArguments)
+ .map(Arguments::get)
+ .map(ParameterizedTemplate::new);
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to generate test templates for class " + testClass.getName(), e);
+ }
+ }
+
+ private List<Method> getParametersMethods(Class<?> testClass) {
+ List<Method> parameters = java.util.stream.Stream.of(testClass.getDeclaredMethods())
+ .filter(m -> Modifier.isStatic(m.getModifiers()))
+ .filter(m -> m.getAnnotation(Parameters.class) != null)
+ .collect(Collectors.toList());
+ if (parameters.isEmpty() && testClass != null) {
+ return getParametersMethods(testClass.getSuperclass());
+ } else {
+ return parameters;
+ }
+ }
+
+ private static Arguments toArguments(Object item) {
+ // Nothing to do except cast.
+ if (item instanceof Arguments) {
+ return (Arguments) item;
+ }
+ // Pass all multidimensional arrays "as is", in contrast to Object[].
+ // See https://github.com/junit-team/junit5/issues/1665
+ if (ReflectionUtils.isMultidimensionalArray(item)) {
+ return arguments(item);
+ }
+ // Special treatment for one-dimensional reference arrays.
+ // See https://github.com/junit-team/junit5/issues/1665
+ if (item instanceof Object[]) {
+ return arguments((Object[]) item);
+ }
+ // Pass everything else "as is".
+ return arguments(item);
+ }
+
+ public static class ParameterizedTemplate implements TestTemplateInvocationContext {
+
+ private final Object[] params;
+
+ public ParameterizedTemplate(Object[] params) {
+ this.params = params;
+ }
+
+ @Override
+ public String getDisplayName(int invocationIndex) {
+ return "[" + invocationIndex + "] "
+ + java.util.stream.Stream.of(params).map(Object::toString).collect(Collectors.joining(", "));
+ }
+
+ @Override
+ public java.util.List<Extension> getAdditionalExtensions() {
+ return Arrays.asList(
+ (TestInstancePostProcessor) this::postProcessTestInstance);
+ }
+
+ protected void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
+ Class<?> clazz = testInstance.getClass();
+ java.util.List<Field> fields = hierarchy(clazz)
+ .map(Class::getDeclaredFields)
+ .flatMap(Stream::of)
+ .filter(f -> isAnnotated(f, Parameter.class))
+ .sorted(Comparator.comparing(f -> (Integer) f.getAnnotation(Parameter.class).value()))
+ .collect(Collectors.toList());
+ if (params.length != fields.size()) {
+ throw new TestInstantiationException(
+ "Expected " + fields.size() + " parameters bug got " + params.length + " when instantiating "
+ + clazz.getName());
+ }
+ for (int i = 0; i < fields.size(); i++) {
+ Field f = fields.get(i);
+ f.setAccessible(true);
+ f.set(testInstance, DefaultArgumentConverter.INSTANCE.convert(params[i], f.getType()));
+ }
+ }
+
+ protected Stream<Class<?>> hierarchy(Class<?> clazz) {
+ Class<?> superclass = clazz.getSuperclass();
+ return Stream.concat(Stream.of(clazz), superclass != null ? hierarchy(superclass) : Stream.empty());
+ }
+
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/Parameters.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/Parameters.java
new file mode 100644
index 0000000..cabf2e5
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/Parameters.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.params;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Parameters {
+ String name() default "{index}";
+}
diff --git a/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/Test.java b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/Test.java
new file mode 100644
index 0000000..d00bcdc
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/main/java/org/apache/camel/test/junit5/params/Test.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.params;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import org.junit.jupiter.api.TestTemplate;
+
+@Retention(RetentionPolicy.RUNTIME)
+@TestTemplate
+public @interface Test {
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/AvailablePortFinderTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/AvailablePortFinderTest.java
new file mode 100644
index 0000000..c4fdf09
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/AvailablePortFinderTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test;
+
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.MulticastSocket;
+import java.net.ServerSocket;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+public class AvailablePortFinderTest {
+
+ @Test
+ public void testNotAvailableTcpPort() throws Exception {
+ int p1 = AvailablePortFinder.getNextAvailable();
+ ServerSocket socket = new ServerSocket(p1);
+ int p2 = AvailablePortFinder.getNextAvailable();
+ assertFalse(p1 == p2, "Port " + p1 + " Port2 " + p2);
+ socket.close();
+ }
+
+ @Test
+ public void testNotAvailableUdpPort() throws Exception {
+ int p1 = AvailablePortFinder.getNextAvailable();
+ DatagramSocket socket = new DatagramSocket(p1);
+ int p2 = AvailablePortFinder.getNextAvailable();
+ assertFalse(p1 == p2, "Port " + p1 + " Port2 " + p2);
+ socket.close();
+ }
+
+ @Test
+ public void testNotAvailableMulticastPort() throws Exception {
+ int p1 = AvailablePortFinder.getNextAvailable();
+ MulticastSocket socket = new MulticastSocket(null);
+ socket.setReuseAddress(false); // true is default for MulticastSocket, we wan to fail if port is occupied
+ socket.bind(new InetSocketAddress(InetAddress.getLocalHost(), p1));
+ int p2 = AvailablePortFinder.getNextAvailable();
+ assertFalse(p1 == p2, "Port " + p1 + " Port2 " + p2);
+ socket.close();
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/CamelTestSupportTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/CamelTestSupportTest.java
new file mode 100644
index 0000000..4c8453f
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/CamelTestSupportTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5;
+
+import org.apache.camel.NoSuchEndpointException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class CamelTestSupportTest extends CamelTestSupport {
+
+ @Override
+ @BeforeEach
+ public void setUp() throws Exception {
+ replaceRouteFromWith("routeId", "direct:start");
+ super.setUp();
+ }
+
+ @Test
+ public void replacesFromEndpoint() throws Exception {
+ getMockEndpoint("mock:result").expectedBodiesReceived("Hello World");
+
+ template.sendBody("direct:start", "Hello World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Test
+ public void exceptionThrownWhenEndpointNotFoundAndNoCreate() {
+ assertThrows(NoSuchEndpointException.class, () -> {
+ getMockEndpoint("mock:bogus", false);
+ });
+ }
+
+ @Test
+ public void exceptionThrownWhenEndpointNotAMockEndpoint() {
+ assertThrows(NoSuchEndpointException.class, () -> {
+ getMockEndpoint("direct:something", false);
+ });
+ }
+
+ @Test
+ public void autoCreateNonExisting() {
+ MockEndpoint mock = getMockEndpoint("mock:bogus2", true);
+ assertNotNull(mock);
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:something").id("routeId").to("mock:result");
+ }
+ };
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternExcludeTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternExcludeTest.java
new file mode 100644
index 0000000..378c71a
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternExcludeTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class RouteFilterPatternExcludeTest extends CamelTestSupport {
+
+ @Override
+ public String getRouteFilterExcludePattern() {
+ return "bar*";
+ }
+
+ @Test
+ public void testRouteFilter() throws Exception {
+ assertEquals(1, context.getRoutes().size());
+ assertEquals(1, context.getRouteDefinitions().size());
+ assertEquals("foo", context.getRouteDefinitions().get(0).getId());
+
+ getMockEndpoint("mock:foo").expectedMessageCount(1);
+
+ template.sendBody("direct:foo", "Hello World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:foo").routeId("foo").to("mock:foo");
+
+ from("direct:bar").routeId("bar").to("mock:bar");
+ }
+ };
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeExcludeTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeExcludeTest.java
new file mode 100644
index 0000000..57e72f4
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeExcludeTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class RouteFilterPatternIncludeExcludeTest extends CamelTestSupport {
+
+ @Override
+ public String getRouteFilterIncludePattern() {
+ return "foo*";
+ }
+
+ @Override
+ public String getRouteFilterExcludePattern() {
+ return "jms:*";
+ }
+
+ @Test
+ public void testRouteFilter() throws Exception {
+ assertEquals(1, context.getRoutes().size());
+ assertEquals(1, context.getRouteDefinitions().size());
+ assertEquals("foo", context.getRouteDefinitions().get(0).getId());
+
+ getMockEndpoint("mock:foo").expectedMessageCount(1);
+
+ template.sendBody("direct:foo", "Hello World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:foo").routeId("foo").to("mock:foo");
+
+ from("direct:bar").routeId("bar").to("mock:bar");
+
+ from("jms:beer").routeId("foolish").to("mock:beer");
+ }
+ };
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeTest.java
new file mode 100644
index 0000000..eb848c7
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/RouteFilterPatternIncludeTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class RouteFilterPatternIncludeTest extends CamelTestSupport {
+
+ @Override
+ public String getRouteFilterIncludePattern() {
+ return "foo*";
+ }
+
+ @Test
+ public void testRouteFilter() throws Exception {
+ assertEquals(1, context.getRoutes().size());
+ assertEquals(1, context.getRouteDefinitions().size());
+ assertEquals("foo", context.getRouteDefinitions().get(0).getId());
+
+ getMockEndpoint("mock:foo").expectedMessageCount(1);
+
+ template.sendBody("direct:foo", "Hello World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:foo").routeId("foo").to("mock:foo");
+
+ from("direct:bar").routeId("bar").to("mock:bar");
+ }
+ };
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AdviceWithLambdaTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AdviceWithLambdaTest.java
new file mode 100644
index 0000000..2df582b
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AdviceWithLambdaTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class AdviceWithLambdaTest extends CamelTestSupport {
+
+ @Override
+ public boolean isUseAdviceWith() {
+ return true;
+ }
+
+ @Test
+ public void testAdviceWith() throws Exception {
+ getMockEndpoint("mock:result").expectedMessageCount(1);
+
+ // advice the route in one line
+ AdviceWith.adviceWith(context, "foo", a -> a.weaveAddLast().to("mock:result"));
+
+ // start Camel
+ context.start();
+
+ template.sendBody("direct:start", "Bye World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start").routeId("foo").to("log:foo");
+ }
+ };
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AdviceWithNotStartedTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AdviceWithNotStartedTest.java
new file mode 100644
index 0000000..7e18af5
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AdviceWithNotStartedTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import java.util.concurrent.RejectedExecutionException;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.builder.AdviceWithRouteBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.test.junit5.TestSupport.assertIsInstanceOf;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class AdviceWithNotStartedTest extends CamelTestSupport {
+
+ @Override
+ public boolean isUseAdviceWith() {
+ return true;
+ }
+
+ @Test
+ public void testNotStarted() throws Exception {
+ AdviceWith.adviceWith(context.getRouteDefinition("foo"), context, new AdviceWithRouteBuilder() {
+ @Override
+ public void configure() {
+ weaveAddLast().to("mock:result");
+ }
+ });
+
+ getMockEndpoint("mock:result").expectedMessageCount(1);
+
+ try {
+ template.sendBody("direct:start", "Hello World");
+ fail("Should throw exception");
+ } catch (CamelExecutionException e) {
+ assertIsInstanceOf(RejectedExecutionException.class, e.getCause());
+ }
+
+ // start Camel
+ context.start();
+
+ template.sendBody("direct:start", "Bye World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start").routeId("foo").to("log:foo");
+ }
+ };
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AsyncSendMockTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AsyncSendMockTest.java
new file mode 100644
index 0000000..b8ce24a
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/AsyncSendMockTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class AsyncSendMockTest extends CamelTestSupport {
+ private static final Logger LOG = LoggerFactory.getLogger(AsyncSendMockTest.class);
+
+ @Override
+ public String isMockEndpoints() {
+ return "seda*";
+ }
+
+ @Test
+ public void testmakeAsyncApiCall() {
+ try {
+ getMockEndpoint("mock:seda:start").expectedHeaderReceived("username", "admin123");
+ getMockEndpoint("mock:seda:start").expectedBodiesReceived("Hello");
+ DefaultExchange dfex = new DefaultExchange(context);
+ dfex.getIn().setHeader("username", "admin123");
+ dfex.getIn().setHeader("password", "admin");
+ dfex.getIn().setBody("Hello");
+ template.asyncSend("seda:start", dfex);
+ assertMockEndpointsSatisfied();
+ } catch (Exception e) {
+ LOG.warn("Failed to make async call to api: {}", e.getMessage(), e);
+ assertTrue(false, "Failed to make async call to api");
+ }
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/CreateCamelContextPerTestFalseTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/CreateCamelContextPerTestFalseTest.java
new file mode 100644
index 0000000..149edb9
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/CreateCamelContextPerTestFalseTest.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@TestInstance(TestInstance.Lifecycle.PER_METHOD)
+public class CreateCamelContextPerTestFalseTest extends CamelTestSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CreateCamelContextPerTestFalseTest.class);
+
+ private static final AtomicInteger CREATED_CONTEXTS = new AtomicInteger();
+ private static final AtomicInteger POST_TEAR_DOWN = new AtomicInteger();
+
+ @EndpointInject("mock:result")
+ protected MockEndpoint resultEndpoint;
+
+ @Produce("direct:start")
+ protected ProducerTemplate template;
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ LOG.info("createCamelContext()");
+ CREATED_CONTEXTS.incrementAndGet();
+ return super.createCamelContext();
+ }
+
+ @Override
+ protected void doPostTearDown() throws Exception {
+ LOG.info("doPostTearDown()");
+ POST_TEAR_DOWN.incrementAndGet();
+ super.doPostTearDown();
+ }
+
+ @Test
+ public void testSendMatchingMessage() throws Exception {
+ String expectedBody = "<matched/>";
+
+ resultEndpoint.expectedBodiesReceived(expectedBody);
+
+ template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+ resultEndpoint.assertIsSatisfied();
+
+ assertTrue(CREATED_CONTEXTS.get() >= 1, "Should create 1 or more CamelContext per test class");
+ }
+
+ @Test
+ public void testSendAnotherMatchingMessage() throws Exception {
+ String expectedBody = "<matched/>";
+
+ resultEndpoint.expectedBodiesReceived(expectedBody);
+
+ template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+ resultEndpoint.assertIsSatisfied();
+
+ assertTrue(CREATED_CONTEXTS.get() >= 1, "Should create 1 or more CamelContext per test class");
+ }
+
+ @Test
+ public void testSendNotMatchingMessage() throws Exception {
+ resultEndpoint.expectedMessageCount(0);
+
+ template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
+
+ resultEndpoint.assertIsSatisfied();
+
+ assertTrue(CREATED_CONTEXTS.get() >= 1, "Should create 1 or more CamelContext per test class");
+ }
+
+ @AfterAll
+ public static void validateTearDown() {
+ assertEquals(3, CREATED_CONTEXTS.get());
+ assertEquals(3, POST_TEAR_DOWN.get());
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+ from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+ }
+ };
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/CreateCamelContextPerTestTrueTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/CreateCamelContextPerTestTrueTest.java
new file mode 100644
index 0000000..222fe62
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/CreateCamelContextPerTestTrueTest.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.apache.camel.util.StopWatch;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class CreateCamelContextPerTestTrueTest extends CamelTestSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CreateCamelContextPerTestTrueTest.class);
+
+ private static final AtomicInteger CREATED_CONTEXTS = new AtomicInteger();
+ private static final AtomicInteger POST_TEAR_DOWN = new AtomicInteger();
+ private static final CountDownLatch LATCH = new CountDownLatch(1);
+
+ @EndpointInject("mock:result")
+ protected MockEndpoint resultEndpoint;
+
+ @Produce("direct:start")
+ protected ProducerTemplate template;
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ LOG.info("createCamelContext()");
+ CREATED_CONTEXTS.incrementAndGet();
+ return super.createCamelContext();
+ }
+
+ @Override
+ protected void doPostTearDown() throws Exception {
+ LOG.info("doPostTearDown()");
+ POST_TEAR_DOWN.incrementAndGet();
+ super.doPostTearDown();
+ LATCH.await(5, TimeUnit.SECONDS);
+ }
+
+ @Test
+ public void testSendMatchingMessage() throws Exception {
+ String expectedBody = "<matched/>";
+
+ resultEndpoint.expectedBodiesReceived(expectedBody);
+
+ template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+ resultEndpoint.assertIsSatisfied();
+
+ assertEquals(1, CREATED_CONTEXTS.get(), "Should only create 1 CamelContext per test class");
+ assertEquals(0, POST_TEAR_DOWN.get(), "Should not call postTearDown yet");
+ }
+
+ @Test
+ public void testSendAnotherMatchingMessage() throws Exception {
+ String expectedBody = "<matched/>";
+
+ resultEndpoint.expectedBodiesReceived(expectedBody);
+
+ template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+ resultEndpoint.assertIsSatisfied();
+
+ assertEquals(1, CREATED_CONTEXTS.get(), "Should only create 1 CamelContext per test class");
+ assertEquals(0, POST_TEAR_DOWN.get(), "Should not call postTearDown yet");
+ }
+
+ @Test
+ public void testSendNotMatchingMessage() throws Exception {
+ resultEndpoint.expectedMessageCount(0);
+
+ template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
+
+ resultEndpoint.assertIsSatisfied();
+
+ assertEquals(1, CREATED_CONTEXTS.get(), "Should only create 1 CamelContext per test class");
+ assertEquals(0, POST_TEAR_DOWN.get(), "Should not call postTearDown yet");
+ }
+
+ @AfterAll
+ public static void shouldTearDown() {
+ // we are called before doPostTearDown so lets wait for that to be
+ // called
+ Runnable r = () -> {
+ try {
+ StopWatch watch = new StopWatch();
+ while (watch.taken() < 5000) {
+ LOG.debug("Checking for tear down called correctly");
+ if (POST_TEAR_DOWN.get() == 1) {
+ LATCH.countDown();
+ break;
+ } else {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+ } finally {
+ LOG.info("Should only call postTearDown 1 time per test class, called: " + POST_TEAR_DOWN.get());
+ assertEquals(1, POST_TEAR_DOWN.get(), "Should only call postTearDown 1 time per test class");
+ }
+ };
+ Thread t = new Thread(r);
+ t.setDaemon(false);
+ t.setName("shouldTearDown checker");
+ t.start();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+ from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+ }
+ };
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugJUnit5Test.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugJUnit5Test.java
new file mode 100644
index 0000000..33a5915
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugJUnit5Test.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DebugJUnit5Test extends CamelTestSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DebugJUnit5Test.class);
+
+ // START SNIPPET: e1
+ @Override
+ public boolean isUseDebugger() {
+ // must enable debugger
+ return true;
+ }
+
+ @Override
+ protected void debugBefore(
+ Exchange exchange, Processor processor, ProcessorDefinition<?> definition, String id, String shortName) {
+ // this method is invoked before we are about to enter the given
+ // processor
+ // from your Java editor you can just add a breakpoint in the code line
+ // below
+ LOG.info("Before " + definition + " with body " + exchange.getIn().getBody());
+ }
+ // END SNIPPET: e1
+
+ @Test
+ public void testDebugger() throws Exception {
+ // set mock expectations
+ getMockEndpoint("mock:a").expectedMessageCount(1);
+ getMockEndpoint("mock:b").expectedMessageCount(1);
+
+ // send a message
+ template.sendBody("direct:start", "World");
+
+ // assert mocks
+ assertMockEndpointsSatisfied();
+ }
+
+ @Test
+ public void testTwo() throws Exception {
+ // set mock expectations
+ getMockEndpoint("mock:a").expectedMessageCount(2);
+ getMockEndpoint("mock:b").expectedMessageCount(2);
+
+ // send a message
+ template.sendBody("direct:start", "World");
+ template.sendBody("direct:start", "Camel");
+
+ // assert mocks
+ assertMockEndpointsSatisfied();
+ }
+
+ // START SNIPPET: e2
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ // this is the route we want to debug
+ from("direct:start").to("mock:a").transform(body().prepend("Hello ")).to("mock:b");
+ }
+ };
+ }
+ // END SNIPPET: e2
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugNoLazyTypeConverterTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugNoLazyTypeConverterTest.java
new file mode 100644
index 0000000..2f894b9
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugNoLazyTypeConverterTest.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DebugNoLazyTypeConverterTest extends CamelTestSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DebugNoLazyTypeConverterTest.class);
+
+ // START SNIPPET: e1
+ @Override
+ public boolean isUseDebugger() {
+ // must enable debugger
+ return true;
+ }
+
+ @Override
+ protected void debugBefore(
+ Exchange exchange, Processor processor, ProcessorDefinition<?> definition, String id, String shortName) {
+ // this method is invoked before we are about to enter the given
+ // processor
+ // from your Java editor you can just add a breakpoint in the code line
+ // below
+ LOG.info("Before " + definition + " with body " + exchange.getIn().getBody());
+ }
+ // END SNIPPET: e1
+
+ @Test
+ public void testDebugger() throws Exception {
+ // set mock expectations
+ getMockEndpoint("mock:a").expectedMessageCount(1);
+ getMockEndpoint("mock:b").expectedMessageCount(1);
+
+ // send a message
+ template.sendBody("direct:start", "World");
+
+ // assert mocks
+ assertMockEndpointsSatisfied();
+ }
+
+ @Test
+ public void testTwo() throws Exception {
+ // set mock expectations
+ getMockEndpoint("mock:a").expectedMessageCount(2);
+ getMockEndpoint("mock:b").expectedMessageCount(2);
+
+ // send a message
+ template.sendBody("direct:start", "World");
+ template.sendBody("direct:start", "Camel");
+
+ // assert mocks
+ assertMockEndpointsSatisfied();
+ }
+
+ // START SNIPPET: e2
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ // this is the route we want to debug
+ from("direct:start").to("mock:a").transform(body().prepend("Hello ")).to("mock:b");
+ }
+ };
+ }
+ // END SNIPPET: e2
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugTest.java
new file mode 100644
index 0000000..c0fff80
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/DebugTest.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.ProcessorDefinition;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DebugTest extends CamelTestSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DebugTest.class);
+
+ // START SNIPPET: e1
+ @Override
+ public boolean isUseDebugger() {
+ // must enable debugger
+ return true;
+ }
+
+ @Override
+ protected void debugBefore(
+ Exchange exchange, Processor processor, ProcessorDefinition<?> definition, String id, String shortName) {
+ // this method is invoked before we are about to enter the given
+ // processor
+ // from your Java editor you can just add a breakpoint in the code line
+ // below
+ LOG.info("Before " + definition + " with body " + exchange.getIn().getBody());
+ }
+ // END SNIPPET: e1
+
+ @Test
+ public void testDebugger() throws Exception {
+ // set mock expectations
+ getMockEndpoint("mock:a").expectedMessageCount(1);
+ getMockEndpoint("mock:b").expectedMessageCount(1);
+
+ // send a message
+ template.sendBody("direct:start", "World");
+
+ // assert mocks
+ assertMockEndpointsSatisfied();
+ }
+
+ @Test
+ public void testTwo() throws Exception {
+ // set mock expectations
+ getMockEndpoint("mock:a").expectedMessageCount(2);
+ getMockEndpoint("mock:b").expectedMessageCount(2);
+
+ // send a message
+ template.sendBody("direct:start", "World");
+ template.sendBody("direct:start", "Camel");
+
+ // assert mocks
+ assertMockEndpointsSatisfied();
+ }
+
+ // START SNIPPET: e2
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ // this is the route we want to debug
+ from("direct:start").to("mock:a").transform(body().prepend("Hello ")).to("mock:b");
+ }
+ };
+ }
+ // END SNIPPET: e2
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterCreateCamelContextPerClassTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterCreateCamelContextPerClassTest.java
new file mode 100644
index 0000000..be38b86
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterCreateCamelContextPerClassTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+
+/**
+ * Tests filtering using Camel Test
+ */
+// START SNIPPET: example
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class FilterCreateCamelContextPerClassTest extends CamelTestSupport {
+
+ @Test
+ public void testSendMatchingMessage() throws Exception {
+ String expectedBody = "<matched/>";
+
+ getMockEndpoint("mock:result").expectedBodiesReceived(expectedBody);
+
+ template.sendBodyAndHeader("direct:start", expectedBody, "foo", "bar");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Test
+ public void testSendNotMatchingMessage() throws Exception {
+ getMockEndpoint("mock:result").expectedMessageCount(0);
+
+ template.sendBodyAndHeader("direct:start", "<notMatched/>", "foo", "notMatchedHeaderValue");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+ from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+ }
+ };
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterFluentTemplateTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterFluentTemplateTest.java
new file mode 100644
index 0000000..e4cef04
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterFluentTemplateTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.FluentProducerTemplate;
+import org.apache.camel.Produce;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests filtering using Camel Test
+ */
+// START SNIPPET: example
+// tag::example[]
+public class FilterFluentTemplateTest extends CamelTestSupport {
+
+ @EndpointInject("mock:result")
+ protected MockEndpoint resultEndpoint;
+
+ @Produce("direct:start")
+ protected FluentProducerTemplate fluentTemplate;
+
+ @Override
+ public boolean isDumpRouteCoverage() {
+ return true;
+ }
+
+ @Test
+ public void testSendMatchingMessage() throws Exception {
+ String expectedBody = "<matched/>";
+
+ resultEndpoint.expectedBodiesReceived(expectedBody);
+
+ fluentTemplate.withBody(expectedBody).withHeader("foo", "bar").send();
+
+ resultEndpoint.assertIsSatisfied();
+ }
+
+ @Test
+ public void testSendNotMatchingMessage() throws Exception {
+ resultEndpoint.expectedMessageCount(0);
+
+ fluentTemplate.withBody("<notMatched/>").withHeader("foo", "notMatchedHeaderValue").send();
+
+ resultEndpoint.assertIsSatisfied();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+ from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+ }
+ };
+ }
+}
+// end::example[]
+// END SNIPPET: example
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterJUnit5Test.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterJUnit5Test.java
new file mode 100644
index 0000000..e946d6b
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterJUnit5Test.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests filtering using Camel Test
+ */
+// START SNIPPET: example
+public class FilterJUnit5Test extends CamelTestSupport {
+
+ @EndpointInject("mock:result")
+ protected MockEndpoint resultEndpoint;
+
+ @Produce("direct:start")
+ protected ProducerTemplate template;
+
+ @Test
+ public void testSendMatchingMessage() throws Exception {
+ String expectedBody = "<matched/>";
+
+ resultEndpoint.expectedBodiesReceived(expectedBody);
+
+ template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+ resultEndpoint.assertIsSatisfied();
+ }
+
+ @Test
+ public void testSendNotMatchingMessage() throws Exception {
+ resultEndpoint.expectedMessageCount(0);
+
+ template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
+
+ resultEndpoint.assertIsSatisfied();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+ from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+ }
+ };
+ }
+
+}
+// END SNIPPET: example
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterTest.java
new file mode 100644
index 0000000..a2455ce
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/FilterTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests filtering using Camel Test
+ */
+// START SNIPPET: example
+// tag::example[]
+public class FilterTest extends CamelTestSupport {
+
+ @EndpointInject("mock:result")
+ protected MockEndpoint resultEndpoint;
+
+ @Produce("direct:start")
+ protected ProducerTemplate template;
+
+ @Override
+ public boolean isDumpRouteCoverage() {
+ return true;
+ }
+
+ @Test
+ public void testSendMatchingMessage() throws Exception {
+ String expectedBody = "<matched/>";
+
+ resultEndpoint.expectedBodiesReceived(expectedBody);
+
+ template.sendBodyAndHeader(expectedBody, "foo", "bar");
+
+ resultEndpoint.assertIsSatisfied();
+ }
+
+ @Test
+ public void testSendNotMatchingMessage() throws Exception {
+ resultEndpoint.expectedMessageCount(0);
+
+ template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
+
+ resultEndpoint.assertIsSatisfied();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+ from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+ }
+ };
+ }
+}
+// end::example[]
+// END SNIPPET: example
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/GetMockEndpointTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/GetMockEndpointTest.java
new file mode 100644
index 0000000..4709eff
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/GetMockEndpointTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class GetMockEndpointTest extends CamelTestSupport {
+
+ @Test
+ public void testMock() throws Exception {
+ getMockEndpoint("mock:result").expectedBodiesReceived("Hello World");
+
+ template.sendBody("direct:start", "Hello World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start").to("mock:result?failFast=false");
+ }
+ };
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsAndSkipJUnit5Test.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsAndSkipJUnit5Test.java
new file mode 100644
index 0000000..2e6b160
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsAndSkipJUnit5Test.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.seda.SedaEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+// START SNIPPET: e1
+// tag::e1[]
+public class IsMockEndpointsAndSkipJUnit5Test extends CamelTestSupport {
+
+ @Override
+ public String isMockEndpointsAndSkip() {
+ // override this method and return the pattern for which endpoints to
+ // mock,
+ // and skip sending to the original endpoint.
+ return "direct:foo";
+ }
+
+ @Test
+ public void testMockEndpointAndSkip() throws Exception {
+ // notice we have automatic mocked the direct:foo endpoints and the name
+ // of the endpoints is "mock:uri"
+ getMockEndpoint("mock:result").expectedBodiesReceived("Hello World");
+ getMockEndpoint("mock:direct:foo").expectedMessageCount(1);
+
+ template.sendBody("direct:start", "Hello World");
+
+ assertMockEndpointsSatisfied();
+
+ // the message was not send to the direct:foo route and thus not sent to
+ // the seda endpoint
+ SedaEndpoint seda = context.getEndpoint("seda:foo", SedaEndpoint.class);
+ assertEquals(0, seda.getCurrentQueueSize());
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start").to("direct:foo").to("mock:result");
+
+ from("direct:foo").transform(constant("Bye World")).to("seda:foo");
+ }
+ };
+ }
+}
+// end::e1[]
+// END SNIPPET: e1
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsFileTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsFileTest.java
new file mode 100644
index 0000000..b8ce79b
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsFileTest.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.junit5.patterns;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.test.junit5.TestSupport.deleteDirectory;
+
+public class IsMockEndpointsFileTest extends CamelTestSupport {
+
+ @Override
+ @BeforeEach
+ public void setUp() throws Exception {
+ deleteDirectory("target/input");
+ deleteDirectory("target/messages");
+ super.setUp();
+ }
+
+ @Override
+ public String isMockEndpoints() {
+ // override this method and return the pattern for which endpoints to
+ // mock.
+ return "file:target*";
+ }
+
+ @Test
+ public void testMockFileEndpoints() throws Exception {
+ // notice we have automatic mocked all endpoints and the name of the
+ // endpoints is "mock:uri"
+ MockEndpoint camel = getMockEndpoint("mock:file:target/messages/camel");
+ camel.expectedMessageCount(1);
+
+ MockEndpoint other = getMockEndpoint("mock:file:target/messages/others");
+ other.expectedMessageCount(1);
+
+ template.sendBodyAndHeader("file:target/input", "Hello Camel", Exchange.FILE_NAME, "camel.txt");
+ template.sendBodyAndHeader("file:target/input", "Hello World", Exchange.FILE_NAME, "world.txt");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("file:target/input").choice().when(bodyAs(String.class).contains("Camel")).to("file:target/messages/camel")
+ .otherwise().to("file:target/messages/others");
+ }
+ };
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsJUnit5Test.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsJUnit5Test.java
new file mode 100644
index 0000000..c131d8c
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsJUnit5Test.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+// START SNIPPET: e1
+// tag::e1[]
+public class IsMockEndpointsJUnit5Test extends CamelTestSupport {
+
+ @Override
+ public String isMockEndpoints() {
+ // override this method and return the pattern for which endpoints to
+ // mock.
+ // use * to indicate all
+ return "*";
+ }
+
+ @Test
+ public void testMockAllEndpoints() throws Exception {
+ // notice we have automatic mocked all endpoints and the name of the
+ // endpoints is "mock:uri"
+ getMockEndpoint("mock:direct:start").expectedBodiesReceived("Hello World");
+ getMockEndpoint("mock:direct:foo").expectedBodiesReceived("Hello World");
+ getMockEndpoint("mock:log:foo").expectedBodiesReceived("Bye World");
+ getMockEndpoint("mock:result").expectedBodiesReceived("Bye World");
+
+ template.sendBody("direct:start", "Hello World");
+
+ assertMockEndpointsSatisfied();
+
+ // additional test to ensure correct endpoints in registry
+ assertNotNull(context.hasEndpoint("direct:start"));
+ assertNotNull(context.hasEndpoint("direct:foo"));
+ assertNotNull(context.hasEndpoint("log:foo"));
+ assertNotNull(context.hasEndpoint("mock:result"));
+ // all the endpoints was mocked
+ assertNotNull(context.hasEndpoint("mock:direct:start"));
+ assertNotNull(context.hasEndpoint("mock:direct:foo"));
+ assertNotNull(context.hasEndpoint("mock:log:foo"));
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start").to("direct:foo").to("log:foo").to("mock:result");
+
+ from("direct:foo").transform(constant("Bye World"));
+ }
+ };
+ }
+}
+// end::e1[]
+// END SNIPPET: e1
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsTest.java
new file mode 100644
index 0000000..2bef92a
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/IsMockEndpointsTest.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class IsMockEndpointsTest extends CamelTestSupport {
+
+ @Override
+ public String isMockEndpoints() {
+ return "*";
+ }
+
+ @Test
+ public void testMockAllEndpoints() throws Exception {
+ getMockEndpoint("mock:direct:start").expectedBodiesReceived("Hello World");
+ getMockEndpoint("mock:direct:foo").expectedBodiesReceived("Hello World");
+ getMockEndpoint("mock:log:foo").expectedBodiesReceived("Bye World");
+ getMockEndpoint("mock:result").expectedBodiesReceived("Bye World");
+
+ template.sendBody("direct:start", "Hello World");
+
+ assertMockEndpointsSatisfied();
+
+ // additional test to ensure correct endpoints in registry
+ assertNotNull(context.hasEndpoint("direct:start"));
+ assertNotNull(context.hasEndpoint("direct:foo"));
+ assertNotNull(context.hasEndpoint("log:foo"));
+ assertNotNull(context.hasEndpoint("mock:result"));
+ // all the endpoints was mocked
+ assertNotNull(context.hasEndpoint("mock:direct:start"));
+ assertNotNull(context.hasEndpoint("mock:direct:foo"));
+ assertNotNull(context.hasEndpoint("mock:log:foo"));
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ 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-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MockEndpointFailNoHeaderTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MockEndpointFailNoHeaderTest.java
new file mode 100644
index 0000000..8746df2
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MockEndpointFailNoHeaderTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class MockEndpointFailNoHeaderTest extends CamelTestSupport {
+
+ @EndpointInject("mock:result")
+ protected MockEndpoint resultEndpoint;
+
+ @Produce("direct:start")
+ protected ProducerTemplate template;
+
+ @Override
+ public boolean isDumpRouteCoverage() {
+ return true;
+ }
+
+ @Test
+ public void withHeaderTestCase() throws InterruptedException {
+ String expectedBody = "<matched/>";
+ resultEndpoint.expectedHeaderReceived("foo", "bar");
+ template.sendBodyAndHeader(expectedBody, "foo", "bar");
+ resultEndpoint.assertIsSatisfied();
+ }
+
+ @Test
+ public void noHeaderTestCase() throws InterruptedException {
+ resultEndpoint.expectedHeaderReceived("foo", "bar");
+ resultEndpoint.setResultWaitTime(1); // speedup test
+ resultEndpoint.assertIsNotSatisfied();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+ from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
+ }
+ };
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MyProduceBean.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MyProduceBean.java
new file mode 100644
index 0000000..95d7263
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MyProduceBean.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.Produce;
+
+/**
+ *
+ */
+public class MyProduceBean {
+
+ @Produce("mock:result")
+ MySender sender;
+
+ public void doSomething(String body) {
+ sender.send(body);
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MySender.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MySender.java
new file mode 100644
index 0000000..0188296
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/MySender.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+/**
+ *
+ */
+public interface MySender {
+
+ void send(String body);
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/ProduceBeanTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/ProduceBeanTest.java
new file mode 100644
index 0000000..af2a99e
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/ProduceBeanTest.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ *
+ */
+public class ProduceBeanTest extends CamelTestSupport {
+
+ @Test
+ public void testProduceBean() throws Exception {
+ getMockEndpoint("mock:result").expectedMessageCount(1);
+
+ template.sendBody("direct:start", "Hello World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start").bean(MyProduceBean.class, "doSomething");
+ }
+ };
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/RouteBuilderConfigureExceptionTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/RouteBuilderConfigureExceptionTest.java
new file mode 100644
index 0000000..d1ab53b
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/RouteBuilderConfigureExceptionTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.Predicate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class RouteBuilderConfigureExceptionTest extends CamelTestSupport {
+
+ private Predicate iAmNull;
+
+ @Override
+ @BeforeEach
+ public void setUp() {
+ try {
+ super.setUp();
+ fail("Should have thrown exception");
+ } catch (Exception e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testFoo() {
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+ from("direct:start").choice().when(iAmNull).to("mock:dead");
+ }
+ };
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/RouteProcessorDumpRouteCoverageTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/RouteProcessorDumpRouteCoverageTest.java
new file mode 100644
index 0000000..4262300
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/RouteProcessorDumpRouteCoverageTest.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.junit5.patterns;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.api.TestReporter;
+
+import static org.apache.camel.test.junit5.TestSupport.assertFileExists;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class RouteProcessorDumpRouteCoverageTest extends CamelTestSupport {
+
+ @Override
+ public boolean isDumpRouteCoverage() {
+ return true;
+ }
+
+ @Test
+ public void testProcessorJunit5() {
+ String out = template.requestBody("direct:start", "Hello World", String.class);
+ assertEquals("Bye World", out);
+ }
+
+ @Test
+ public void testProcessorJunit5WithTestParameterInjection(TestInfo info, TestReporter testReporter) {
+ assertNotNull(info);
+ assertNotNull(testReporter);
+ String out = template.requestBody("direct:start", "Hello World", String.class);
+ assertEquals("Bye World", out);
+ }
+
+ @AfterAll
+ public static void checkDumpFilesCreatedAfterTests() {
+ // should create that file when test is done
+ assertFileExists("target/camel-route-coverage/RouteProcessorDumpRouteCoverageTest-testProcessorJunit5.xml");
+ assertFileExists(
+ "target/camel-route-coverage/RouteProcessorDumpRouteCoverageTest-testProcessorJunit5WithTestParameterInjection.xml");
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start").process(exchange -> exchange.getMessage().setBody("Bye World"));
+ }
+ };
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleMockEndpointsTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleMockEndpointsTest.java
new file mode 100644
index 0000000..40f2f3b
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleMockEndpointsTest.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class SimpleMockEndpointsTest extends CamelTestSupport {
+
+ @Override
+ public String isMockEndpointsAndSkip() {
+ return "seda:queue";
+ }
+
+ @Test
+ public void testMockAndSkip() throws Exception {
+ getMockEndpoint("mock:seda:queue").expectedBodiesReceived("Bye Camel");
+
+ template.sendBody("seda:start", "Camel");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("seda:start").transform(simple("Bye ${body}")).to("seda:queue");
+ }
+ };
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleMockTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleMockTest.java
new file mode 100644
index 0000000..be5ed41
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleMockTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class SimpleMockTest extends CamelTestSupport {
+
+ @Test
+ public void testMock() throws Exception {
+ getMockEndpoint("mock:result").expectedBodiesReceived("Hello World");
+
+ template.sendBody("direct:start", "Hello World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start").to("mock:result");
+ }
+ };
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleNotifyBuilderTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleNotifyBuilderTest.java
new file mode 100644
index 0000000..3dbef5a
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleNotifyBuilderTest.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.NotifyBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class SimpleNotifyBuilderTest extends CamelTestSupport {
+
+ @Test
+ public void testNotifyBuilder() {
+ NotifyBuilder notify = new NotifyBuilder(context).from("seda:start").wereSentTo("seda:queue").whenDone(10).create();
+
+ for (int i = 0; i < 10; i++) {
+ template.sendBody("seda:start", "Camel" + i);
+ }
+
+ boolean matches = notify.matches(10, TimeUnit.SECONDS);
+ assertTrue(matches);
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("seda:start").transform(simple("Bye ${body}")).to("seda:queue");
+ }
+ };
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleWeaveAddMockLastTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleWeaveAddMockLastTest.java
new file mode 100644
index 0000000..4293758
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/SimpleWeaveAddMockLastTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.builder.AdviceWithRouteBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+public class SimpleWeaveAddMockLastTest extends CamelTestSupport {
+
+ public boolean isUseAdviceWith() {
+ return true;
+ }
+
+ @Test
+ public void testWeaveAddMockLast() throws Exception {
+ AdviceWith.adviceWith(context.getRouteDefinitions().get(0), context, new AdviceWithRouteBuilder() {
+ @Override
+ public void configure() {
+ weaveAddLast().to("mock:result");
+ }
+ });
+ context.start();
+
+ getMockEndpoint("mock:result").expectedBodiesReceived("Bye Camel");
+
+ template.sendBody("seda:start", "Camel");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("seda:start").transform(simple("Bye ${body}")).to("seda:queue");
+ }
+ };
+ }
+
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/UseOverridePropertiesWithPropertiesComponentTest.java b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/UseOverridePropertiesWithPropertiesComponentTest.java
new file mode 100644
index 0000000..761ae43
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/java/org/apache/camel/test/junit5/patterns/UseOverridePropertiesWithPropertiesComponentTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.junit5.patterns;
+
+import java.util.Properties;
+
+import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.builder.AdviceWithRouteBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class UseOverridePropertiesWithPropertiesComponentTest extends CamelTestSupport {
+
+ @Override
+ public boolean isUseAdviceWith() {
+ return true;
+ }
+
+ @BeforeEach
+ public void doSomethingBefore() throws Exception {
+ AdviceWithRouteBuilder mocker = new AdviceWithRouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ replaceFromWith("direct:sftp");
+
+ interceptSendToEndpoint("file:*").skipSendToOriginalEndpoint().to("mock:file");
+ }
+ };
+ AdviceWith.adviceWith(context.getRouteDefinition("myRoute"), context, mocker);
+ }
+
+ @Override
+ protected Properties useOverridePropertiesWithPropertiesComponent() {
+ Properties pc = new Properties();
+ pc.put("ftp.username", "scott");
+ pc.put("ftp.password", "tiger");
+ return pc;
+ }
+
+ @Test
+ public void testOverride() throws Exception {
+ context.start();
+
+ getMockEndpoint("mock:file").expectedMessageCount(1);
+
+ template.sendBody("direct:sftp", "Hello World");
+
+ assertMockEndpointsSatisfied();
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+ from("ftp:somepath?username={{ftp.username}}&password={{ftp.password}}").routeId("myRoute")
+ .to("file:target/out");
+ }
+ };
+ }
+}
diff --git a/components/camel-test/camel-test-junit5/src/test/resources/log4j2.properties b/components/camel-test/camel-test-junit5/src/test/resources/log4j2.properties
new file mode 100644
index 0000000..53994e6
--- /dev/null
+++ b/components/camel-test/camel-test-junit5/src/test/resources/log4j2.properties
@@ -0,0 +1,30 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements. See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-test.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d %-5p %c{1} - %m %n
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = [%30.30t] %-30.30c{1} %-5p %m%n
+logger.springframework.name = org.springframework
+logger.springframework.level = WARN
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
diff --git a/components/camel-test/camel-test-spring-junit5/pom.xml b/components/camel-test/camel-test-spring-junit5/pom.xml
new file mode 100644
index 0000000..4e9eb7e
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/pom.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test-parent</artifactId>
+ <version>3.9.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-test-spring-junit5</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Camel :: Test :: Spring:: JUnit5</name>
+ <description>Camel unit testing with Spring and JUnit 5</description>
+
+ <properties>
+ <firstVersion>3.0.0</firstVersion>
+ <label>testing,java,spring</label>
+ <title>Test Spring JUnit5</title>
+ <spring-version>${spring5-version}</spring-version>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test-junit5</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-spring-xml</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ </dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-management</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-slf4j-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/components/camel-test/camel-test-spring-junit5/src/generated/resources/META-INF/services/org/apache/camel/other.properties b/components/camel-test/camel-test-spring-junit5/src/generated/resources/META-INF/services/org/apache/camel/other.properties
new file mode 100644
index 0000000..ea7d2ba
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/generated/resources/META-INF/services/org/apache/camel/other.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+name=test-spring-junit5
+groupId=org.apache.camel
+artifactId=camel-test-spring-junit5
+version=3.9.0-SNAPSHOT
+projectName=Camel :: Test :: Spring:: JUnit5
+projectDescription=Camel unit testing with Spring and JUnit 5
diff --git a/components/camel-test/camel-test-spring-junit5/src/generated/resources/test-spring-junit5.json b/components/camel-test/camel-test-spring-junit5/src/generated/resources/test-spring-junit5.json
new file mode 100644
index 0000000..d86be98
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/generated/resources/test-spring-junit5.json
@@ -0,0 +1,15 @@
+{
+ "other": {
+ "kind": "other",
+ "name": "test-spring-junit5",
+ "title": "Test Spring JUnit5",
+ "description": "Camel unit testing with Spring and JUnit 5",
+ "deprecated": false,
+ "firstVersion": "3.0.0",
+ "label": "testing,java,spring",
+ "supportLevel": "Stable",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-test-spring-junit5",
+ "version": "3.9.0-SNAPSHOT"
+ }
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/docs/test-spring-junit5.adoc b/components/camel-test/camel-test-spring-junit5/src/main/docs/test-spring-junit5.adoc
new file mode 100644
index 0000000..122635f
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/docs/test-spring-junit5.adoc
@@ -0,0 +1,148 @@
+[[test-spring-junit5-other]]
+= Test Spring JUnit5 Component
+:docTitle: Test Spring JUnit5
+:artifactId: camel-test-spring-junit5
+:description: Camel unit testing with Spring and JUnit 5
+:since: 3.0
+:supportLevel: Stable
+include::{cq-version}@camel-quarkus:ROOT:partial$reference/others/test-spring-junit5.adoc[opts=optional]
+
+*Since Camel {since}*
+
+The `camel-test-spring-junit5` module makes it possible to test Camel Spring based applications with JUnit 5.
+
+Testing is a crucial part of any development or integration work. The Spring Framework offers a number of features that makes it easy to test while using Spring for Inversion of Control.
+
+There are multiple approaches to test Camel Spring 5.x based routes with JUnit 5.
+
+== Extending the CamelSpringTestSupport class
+An approach is to extend `org.apache.camel.test.spring.junit5.CamelSpringTestSupport`, for instance:
+----
+public class SimpleMockTest extends CamelSpringTestSupport {
+
+ @EndpointInject("mock:result")
+ protected MockEndpoint resultEndpoint;
+
+ @Produce("direct:start")
+ protected ProducerTemplate template;
+
+ @Override
+ protected AbstractApplicationContext createApplicationContext() {
+ return new ClassPathXmlApplicationContext("org/apache/camel/test/patterns/SimpleMockTest.xml");
+ }
+
+ @Test
+ public void testMock() throws Exception {
+ String expectedBody = "Hello World";
+ resultEndpoint.expectedBodiesReceived(expectedBody);
+ template.sendBodyAndHeader(expectedBody, "foo", "bar");
+ resultEndpoint.assertIsSatisfied();
+ }
+}
+----
+
+This approach provides feature parity with `org.apache.camel.test.junit5.CamelTestSupport` from xref:test-junit5.adoc[camel-test-junit5] but does not support Spring annotations on the test class such as `@Autowired`, `@DirtiesContext`, and `@ContextConfiguration`.
+
+Instead of instantiating the `CamelContext` and routes programmatically, this class relies on a Spring context to wire the needed components together. If your test extends this class, you must provide the Spring context by implementing the following method.
+[source,java]
+----
+protected abstract AbstractApplicationContext createApplicationContext();
+----
+
+== Using the @CamelSpringTest annotation
+Another approach involves the usage of the `org.apache.camel.test.spring.junit5.CamelSpringTest` annotation, for instance:
+----
+@CamelSpringTest
+@ContextConfiguration
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+public class CamelSpringPlainTest {
+
+ @Autowired
+ protected CamelContext camelContext;
+
+ @EndpointInject("mock:a")
+ protected MockEndpoint mockA;
+
+ @EndpointInject("mock:b")
+ protected MockEndpoint mockB;
+
+ @Produce("direct:start")
+ protected ProducerTemplate start;
+
+ @Test
+ public void testPositive() throws Exception {
+ assertEquals(ServiceStatus.Started, camelContext.getStatus());
+
+ mockA.expectedBodiesReceived("David");
+ mockB.expectedBodiesReceived("Hello David");
+
+ start.sendBody("David");
+
+ MockEndpoint.assertIsSatisfied(camelContext);
+ }
+
+ @Test
+ public void testAnotherCase() throws Exception {
+ ...
+ }
+}
+----
+
+The above test will load the route from `org/apache/camel/test/spring/CamelSpringPlainTest-context.xml` which looks like below:
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd ">
+
+ <camelContext id="camelContext"
+ xmlns="http://camel.apache.org/schema/spring" trace="true"
+ autoStartup="true">
+ <packageScan>
+ <package>org.apache.camel.test.spring</package>
+ </packageScan>
+ <route>
+ <from uri="direct:start" />
+ <to uri="mock:a" />
+ <transform>
+ <simple>Hello ${body}</simple>
+ </transform>
+ <to uri="mock:b" />
+ </route>
+ </camelContext>
+
+ <bean id="bridgePropertyPlaceholder"
+ class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
+ <property name="location"
+ value="classpath:org/apache/camel/test/spring/test.properties" />
+ </bean>
+</beans>
+----
+
+This approach supports both Camel and Spring annotations, such as `@Autowired`, `@DirtiesContext`, and `@ContextConfiguration`.
+However, it does NOT have feature parity with `org.apache.camel.test.junit5.CamelTestSupport`.
+
+== Migrating Camel Spring Tests from JUnit 4 to JUnit 5
+Find below some hints to help in migrating Camel Spring tests from JUnit 4 to JUnit 5.
+
+=== Referencing the Camel Test Spring JUnit 5 library in your project
+Projects using `camel-test-spring` would need to use `camel-test-spring-junit5`. For instance, maven users would update their pom.xml file as below:
+----
+<dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test-spring-junit5</artifactId>
+ <scope>test</scope>
+</dependency>
+----
+
+Tips: It's possible to run JUnit 4 & JUnit 5 based Camel Spring tests side by side including the following dependencies `camel-test-spring`,
+`camel-test-spring-junit5` and `junit-vintage-engine`. This configuration allows to migrate a Camel Spring test at once.
+
+=== Typical migration steps linked to JUnit 5 support in Camel Test Spring
+* Migration steps linked to xref:test-junit5.adoc[JUnit 5 support in Camel Test itself] should have been applied first
+* Imports of `org.apache.camel.test.spring.\*` should be replaced with `org.apache.camel.test.spring.junit5.*`
+* Usage of `@RunWith(CamelSpringRunner.class)` should be replaced with `@CamelSpringTest`
+* Usage of `@BootstrapWith(CamelTestContextBootstrapper.class)` should be replaced with `@CamelSpringTest`
+* Usage of `@RunWith(CamelSpringBootRunner.class)` should be replaced with `@CamelSpringBootTest`
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelAnnotationsHandler.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelAnnotationsHandler.java
new file mode 100644
index 0000000..f11f331
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelAnnotationsHandler.java
@@ -0,0 +1,409 @@
+/*
+ * 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.spring.junit5;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+import java.util.StringJoiner;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.api.management.ManagedCamelContext;
+import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
+import org.apache.camel.component.mock.InterceptSendToMockEndpointStrategy;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.impl.debugger.DefaultDebugger;
+import org.apache.camel.spi.Breakpoint;
+import org.apache.camel.spi.Debugger;
+import org.apache.camel.spi.EventNotifier;
+import org.apache.camel.spi.PropertiesComponent;
+import org.apache.camel.spring.SpringCamelContext;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.annotation.AnnotationUtils;
+
+public final class CamelAnnotationsHandler {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CamelAnnotationsHandler.class);
+
+ private CamelAnnotationsHandler() {
+ }
+
+ /**
+ * Cleanup/restore global state to defaults / pre-test values after the test setup is complete.
+ *
+ * @param testClass the test class being executed
+ */
+ public static void cleanup(Class<?> testClass) {
+ DefaultCamelContext.clearOptions();
+ }
+
+ /**
+ * Handles @ExcludeRoutes to make it easier to exclude other routes when testing with Spring.
+ *
+ * @param context the initialized Spring context
+ * @param testClass the test class being executed
+ */
+ public static void handleExcludeRoutes(ConfigurableApplicationContext context, Class<?> testClass) {
+ String key = SpringCamelContext.EXCLUDE_ROUTES;
+ String exists = System.getProperty(key);
+ if (exists != null) {
+ LOGGER.warn("The JVM property " + key + " is set, but not supported anymore.");
+ }
+
+ if (testClass.isAnnotationPresent(ExcludeRoutes.class)) {
+ Class<?>[] routes = testClass.getAnnotation(ExcludeRoutes.class).value();
+ // need to setup this as a JVM system property
+ StringJoiner routesBuilder = new StringJoiner(",");
+ for (Class<?> clazz : routes) {
+ routesBuilder.add(clazz.getName());
+ }
+ String value = routesBuilder.toString();
+ LOGGER.info("@ExcludeRoutes annotation found. Setting up JVM property {}={}", key, value);
+ DefaultCamelContext.setExcludeRoutes(value);
+ }
+ }
+
+ /**
+ * Handles disabling of JMX on Camel contexts based on {@link DisableJmx}.
+ *
+ * @param context the initialized Spring context
+ * @param testClass the test class being executed
+ */
+ public static void handleDisableJmx(ConfigurableApplicationContext context, Class<?> testClass) {
+ if (testClass.isAnnotationPresent(DisableJmx.class)) {
+ if (testClass.getAnnotation(DisableJmx.class).value()) {
+ LOGGER.info("Disabling Camel JMX globally as DisableJmx annotation was found and disableJmx is set to true.");
+ DefaultCamelContext.setDisableJmx(true);
+ } else {
+ LOGGER.info("Enabling Camel JMX as DisableJmx annotation was found and disableJmx is set to false.");
+ DefaultCamelContext.setDisableJmx(false);
+ }
+ } else if (!testClass.isAnnotationPresent(EnableRouteCoverage.class)) {
+ // route coverage need JMX so do not disable it by default
+ LOGGER.info(
+ "Disabling Camel JMX globally for tests by default. Use the DisableJMX annotation to override the default setting.");
+ DefaultCamelContext.setDisableJmx(true);
+ } else {
+ LOGGER.info("Enabling Camel JMX as EnableRouteCoverage is used.");
+ DefaultCamelContext.setDisableJmx(false);
+ }
+ }
+
+ /**
+ * Handles disabling of JMX on Camel contexts based on {@link EnableRouteCoverage}.
+ *
+ * @param context the initialized Spring context
+ * @param testClass the test class being executed
+ */
+ public static void handleRouteCoverage(ConfigurableApplicationContext context, Class<?> testClass, Function testMethod)
+ throws Exception {
+ if (testClass.isAnnotationPresent(EnableRouteCoverage.class)) {
+ System.setProperty(CamelTestSupport.ROUTE_COVERAGE_ENABLED, "true");
+
+ CamelSpringTestHelper.doToSpringCamelContexts(context, new CamelSpringTestHelper.DoToSpringCamelContextsStrategy() {
+
+ @Override
+ public void execute(String contextName, SpringCamelContext camelContext) throws Exception {
+ LOGGER.info("Enabling RouteCoverage");
+ EventNotifier notifier = new RouteCoverageEventNotifier(testClass.getName(), testMethod);
+ camelContext.addService(notifier, true);
+ camelContext.getManagementStrategy().addEventNotifier(notifier);
+ }
+ });
+ }
+ }
+
+ public static void handleRouteCoverageDump(ConfigurableApplicationContext context, Class<?> testClass, Function testMethod)
+ throws Exception {
+ if (testClass.isAnnotationPresent(EnableRouteCoverage.class)) {
+ CamelSpringTestHelper.doToSpringCamelContexts(context, new CamelSpringTestHelper.DoToSpringCamelContextsStrategy() {
+
+ @Override
+ public void execute(String contextName, SpringCamelContext camelContext) throws Exception {
+ LOGGER.debug("Dumping RouteCoverage");
+
+ String testMethodName = (String) testMethod.apply(this);
+ RouteCoverageDumper.dumpRouteCoverage(camelContext, testClass.getName(), testMethodName);
+
+ // reset JMX statistics
+ ManagedCamelContextMBean managedCamelContext
+ = camelContext.getExtension(ManagedCamelContext.class).getManagedCamelContext();
+ if (managedCamelContext != null) {
+ LOGGER.debug("Resetting JMX statistics for RouteCoverage");
+ managedCamelContext.reset(true);
+ }
+
+ // turn off dumping one more time by removing the event listener (which would dump as well when Camel is stopping)
+ // but this method was explicit invoked to dump such as from afterTest callbacks from JUnit.
+ RouteCoverageEventNotifier eventNotifier = camelContext.hasService(RouteCoverageEventNotifier.class);
+ if (eventNotifier != null) {
+ camelContext.getManagementStrategy().removeEventNotifier(eventNotifier);
+ camelContext.removeService(eventNotifier);
+ }
+ }
+ });
+ }
+ }
+
+ public static void handleProvidesBreakpoint(ConfigurableApplicationContext context, Class<?> testClass) throws Exception {
+ Collection<Method> methods = CamelSpringTestHelper.getAllMethods(testClass);
+ final List<Breakpoint> breakpoints = new LinkedList<>();
+
+ for (Method method : methods) {
+ if (AnnotationUtils.findAnnotation(method, ProvidesBreakpoint.class) != null) {
+ Class<?>[] argTypes = method.getParameterTypes();
+ if (argTypes.length != 0) {
+ throw new IllegalArgumentException(
+ "Method [" + method.getName()
+ + "] is annotated with ProvidesBreakpoint but is not a no-argument method.");
+ } else if (!Breakpoint.class.isAssignableFrom(method.getReturnType())) {
+ throw new IllegalArgumentException(
+ "Method [" + method.getName()
+ + "] is annotated with ProvidesBreakpoint but does not return a Breakpoint.");
+ } else if (!Modifier.isStatic(method.getModifiers())) {
+ throw new IllegalArgumentException(
+ "Method [" + method.getName()
+ + "] is annotated with ProvidesBreakpoint but is not static.");
+ } else if (!Modifier.isPublic(method.getModifiers())) {
+ throw new IllegalArgumentException(
+ "Method [" + method.getName()
+ + "] is annotated with ProvidesBreakpoint but is not public.");
+ }
+
+ try {
+ breakpoints.add((Breakpoint) method.invoke(null));
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Method [" + method.getName()
+ + "] threw exception during evaluation.",
+ e);
+ }
+ }
+ }
+
+ if (breakpoints.size() != 0) {
+ CamelSpringTestHelper.doToSpringCamelContexts(context, new CamelSpringTestHelper.DoToSpringCamelContextsStrategy() {
+
+ public void execute(String contextName, SpringCamelContext camelContext)
+ throws Exception {
+ Debugger debugger = camelContext.getDebugger();
+ if (debugger == null) {
+ debugger = new DefaultDebugger();
+ camelContext.setDebugger(debugger);
+ }
+
+ for (Breakpoint breakpoint : breakpoints) {
+ LOGGER.info("Adding Breakpoint [{}] to CamelContext with name [{}].", breakpoint, contextName);
+ debugger.addBreakpoint(breakpoint);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Handles updating shutdown timeouts on Camel contexts based on {@link ShutdownTimeout}.
+ *
+ * @param context the initialized Spring context
+ * @param testClass the test class being executed
+ */
+ public static void handleShutdownTimeout(ConfigurableApplicationContext context, Class<?> testClass) throws Exception {
+ final int shutdownTimeout;
+ final TimeUnit shutdownTimeUnit;
+ if (testClass.isAnnotationPresent(ShutdownTimeout.class)) {
+ shutdownTimeout = testClass.getAnnotation(ShutdownTimeout.class).value();
+ shutdownTimeUnit = testClass.getAnnotation(ShutdownTimeout.class).timeUnit();
+ } else {
+ shutdownTimeout = 10;
+ shutdownTimeUnit = TimeUnit.SECONDS;
+ }
+
+ CamelSpringTestHelper.doToSpringCamelContexts(context, new CamelSpringTestHelper.DoToSpringCamelContextsStrategy() {
+
+ public void execute(String contextName, SpringCamelContext camelContext)
+ throws Exception {
+ LOGGER.info("Setting shutdown timeout to [{} {}] on CamelContext with name [{}].", shutdownTimeout,
+ shutdownTimeUnit, contextName);
+ camelContext.getShutdownStrategy().setTimeout(shutdownTimeout);
+ camelContext.getShutdownStrategy().setTimeUnit(shutdownTimeUnit);
+ }
+ });
+ }
+
+ /**
+ * Handles auto-intercepting of endpoints with mocks based on {@link MockEndpoints}.
+ *
+ * @param context the initialized Spring context
+ * @param testClass the test class being executed
+ */
+ public static void handleMockEndpoints(ConfigurableApplicationContext context, Class<?> testClass) throws Exception {
+ if (testClass.isAnnotationPresent(MockEndpoints.class)) {
+ final String mockEndpoints = testClass.getAnnotation(MockEndpoints.class).value();
+ CamelSpringTestHelper.doToSpringCamelContexts(context, new CamelSpringTestHelper.DoToSpringCamelContextsStrategy() {
+
+ public void execute(String contextName, SpringCamelContext camelContext)
+ throws Exception {
+ LOGGER.info("Enabling auto mocking of endpoints matching pattern [{}] on CamelContext with name [{}].",
+ mockEndpoints, contextName);
+ camelContext.adapt(ExtendedCamelContext.class)
+ .registerEndpointCallback(new InterceptSendToMockEndpointStrategy(mockEndpoints));
+ }
+ });
+ }
+ }
+
+ /**
+ * Handles auto-intercepting of endpoints with mocks based on {@link MockEndpointsAndSkip} and skipping the original
+ * endpoint.
+ *
+ * @param context the initialized Spring context
+ * @param testClass the test class being executed
+ */
+ public static void handleMockEndpointsAndSkip(ConfigurableApplicationContext context, Class<?> testClass) throws Exception {
+ if (testClass.isAnnotationPresent(MockEndpointsAndSkip.class)) {
+ final String mockEndpoints = testClass.getAnnotation(MockEndpointsAndSkip.class).value();
+ CamelSpringTestHelper.doToSpringCamelContexts(context, new CamelSpringTestHelper.DoToSpringCamelContextsStrategy() {
+
+ public void execute(String contextName, SpringCamelContext camelContext)
+ throws Exception {
+ // resolve the property place holders of the mockEndpoints
+ String mockEndpointsValue = camelContext.resolvePropertyPlaceholders(mockEndpoints);
+ LOGGER.info(
+ "Enabling auto mocking and skipping of endpoints matching pattern [{}] on CamelContext with name [{}].",
+ mockEndpointsValue, contextName);
+ camelContext.adapt(ExtendedCamelContext.class)
+ .registerEndpointCallback(new InterceptSendToMockEndpointStrategy(mockEndpointsValue, true));
+ }
+ });
+ }
+ }
+
+ /**
+ * Handles override this method to include and override properties with the Camel
+ * {@link org.apache.camel.component.properties.PropertiesComponent}.
+ *
+ * @param context the initialized Spring context
+ * @param testClass the test class being executed
+ */
+ public static void handleUseOverridePropertiesWithPropertiesComponent(
+ ConfigurableApplicationContext context, Class<?> testClass)
+ throws Exception {
+ Collection<Method> methods = CamelSpringTestHelper.getAllMethods(testClass);
+ final List<Properties> properties = new LinkedList<>();
+
+ for (Method method : methods) {
+ if (AnnotationUtils.findAnnotation(method, UseOverridePropertiesWithPropertiesComponent.class) != null) {
+ Class<?>[] argTypes = method.getParameterTypes();
+ if (argTypes.length > 0) {
+ throw new IllegalArgumentException(
+ "Method [" + method.getName()
+ + "] is annotated with UseOverridePropertiesWithPropertiesComponent but is not a no-argument method.");
+ } else if (!Properties.class.isAssignableFrom(method.getReturnType())) {
+ throw new IllegalArgumentException(
+ "Method [" + method.getName()
+ + "] is annotated with UseOverridePropertiesWithPropertiesComponent but does not return a java.util.Properties.");
+ } else if (!Modifier.isStatic(method.getModifiers())) {
+ throw new IllegalArgumentException(
+ "Method [" + method.getName()
+ + "] is annotated with UseOverridePropertiesWithPropertiesComponent but is not static.");
+ } else if (!Modifier.isPublic(method.getModifiers())) {
+ throw new IllegalArgumentException(
+ "Method [" + method.getName()
+ + "] is annotated with UseOverridePropertiesWithPropertiesComponent but is not public.");
+ }
+
+ try {
+ properties.add((Properties) method.invoke(null));
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Method [" + method.getName()
+ + "] threw exception during evaluation.",
+ e);
+ }
+ }
+ }
+
+ Properties extra = new Properties();
+ for (Properties prop : properties) {
+ extra.putAll(prop);
+ }
+
+ if (!extra.isEmpty()) {
+ context.addBeanFactoryPostProcessor(beanFactory -> beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) {
+ if (bean instanceof PropertiesComponent) {
+ PropertiesComponent pc = (PropertiesComponent) bean;
+ LOGGER.info("Using {} properties to override any existing properties on the PropertiesComponent",
+ extra.size());
+ pc.setOverrideProperties(extra);
+ }
+ return bean;
+ }
+ }));
+ }
+ }
+
+ /**
+ * Handles starting of Camel contexts based on {@link UseAdviceWith} and other state in the JVM.
+ *
+ * @param context the initialized Spring context
+ * @param testClass the test class being executed
+ */
+ public static void handleCamelContextStartup(ConfigurableApplicationContext context, Class<?> testClass) throws Exception {
+ boolean skip = "true".equalsIgnoreCase(System.getProperty("skipStartingCamelContext"));
+ if (skip) {
+ LOGGER.info("Skipping starting CamelContext(s) as system property skipStartingCamelContext is set to be true.");
+ } else if (testClass.isAnnotationPresent(UseAdviceWith.class)) {
+ if (testClass.getAnnotation(UseAdviceWith.class).value()) {
+ LOGGER.info(
+ "Skipping starting CamelContext(s) as UseAdviceWith annotation was found and isUseAdviceWith is set to true.");
+ skip = true;
+ } else {
+ LOGGER.info(
+ "Starting CamelContext(s) as UseAdviceWith annotation was found, but isUseAdviceWith is set to false.");
+ skip = false;
+ }
+ }
+
+ if (!skip) {
+ CamelSpringTestHelper.doToSpringCamelContexts(context, new CamelSpringTestHelper.DoToSpringCamelContextsStrategy() {
+ public void execute(
+ String contextName,
+ SpringCamelContext camelContext)
+ throws Exception {
+ if (!camelContext.isStarted()) {
+ LOGGER.info("Starting CamelContext with name [{}].", contextName);
+ camelContext.start();
+ } else {
+ LOGGER.debug("CamelContext with name [{}] already started.", contextName);
+ }
+ }
+ });
+ }
+ }
+
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringBootExecutionListener.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringBootExecutionListener.java
new file mode 100644
index 0000000..1b836dd
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringBootExecutionListener.java
@@ -0,0 +1,125 @@
+/*
+ * 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.spring.junit5;
+
+import org.apache.camel.spring.SpringCamelContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.test.context.TestContext;
+import org.springframework.test.context.support.AbstractTestExecutionListener;
+
+public class CamelSpringBootExecutionListener extends AbstractTestExecutionListener {
+
+ protected static ThreadLocal<ConfigurableApplicationContext> threadApplicationContext = new ThreadLocal<>();
+
+ private static final Logger LOG = LoggerFactory.getLogger(CamelSpringBootExecutionListener.class);
+ private static final String PROPERTY_SKIP_STARTING_CAMEL_CONTEXT = "skipStartingCamelContext";
+
+ /**
+ * Returns the precedence that is used by Spring to choose the appropriate execution order of test listeners.
+ *
+ * See {@link SpringTestExecutionListenerSorter#getPrecedence(Class)} for more.
+ */
+ @Override
+ public int getOrder() {
+ return SpringTestExecutionListenerSorter.getPrecedence(getClass());
+ }
+
+ @Override
+ public void beforeTestClass(TestContext testContext) throws Exception {
+ // prevent other extensions to start the Camel context
+ preventContextStart();
+ }
+
+ @Override
+ public void prepareTestInstance(TestContext testContext) throws Exception {
+ LOG.info("CamelSpringBootExecutionListener preparing: {}", testContext.getTestClass());
+
+ Class<?> testClass = testContext.getTestClass();
+
+ // need to prepare this before we load spring application context
+ CamelAnnotationsHandler.handleDisableJmx(null, testClass);
+ CamelAnnotationsHandler.handleExcludeRoutes(null, testClass);
+
+ // prevent the Camel context to be started to be able to extend it.
+ preventContextStart();
+ ConfigurableApplicationContext context = (ConfigurableApplicationContext) testContext.getApplicationContext();
+
+ CamelAnnotationsHandler.handleUseOverridePropertiesWithPropertiesComponent(context, testClass);
+
+ // Post CamelContext(s) instantiation but pre CamelContext(s) start
+ // setup
+ CamelAnnotationsHandler.handleProvidesBreakpoint(context, testClass);
+ CamelAnnotationsHandler.handleShutdownTimeout(context, testClass);
+ CamelAnnotationsHandler.handleMockEndpoints(context, testClass);
+ CamelAnnotationsHandler.handleMockEndpointsAndSkip(context, testClass);
+
+ System.clearProperty(PROPERTY_SKIP_STARTING_CAMEL_CONTEXT);
+ SpringCamelContext.setNoStart(false);
+ }
+
+ /**
+ * Sets the {@link SpringCamelContext#setNoStart(boolean)} and the system property
+ * <code>skipStartingCamelContext</code>to <code>true</code> to let us customizing the Camel context with
+ * {@link CamelAnnotationsHandler} before it has been started. It's needed as early as possible to prevent other
+ * extensions to start it <b>and</b> before every test run.
+ */
+ private void preventContextStart() {
+ SpringCamelContext.setNoStart(true);
+ System.setProperty(PROPERTY_SKIP_STARTING_CAMEL_CONTEXT, "true");
+ }
+
+ @Override
+ public void beforeTestMethod(TestContext testContext) throws Exception {
+ LOG.info("CamelSpringBootExecutionListener before: {}.{}", testContext.getTestClass(),
+ testContext.getTestMethod().getName());
+
+ Class<?> testClass = testContext.getTestClass();
+ String testName = testContext.getTestMethod().getName();
+
+ ConfigurableApplicationContext context = (ConfigurableApplicationContext) testContext.getApplicationContext();
+ threadApplicationContext.set(context);
+
+ // mark Camel to be startable again and start Camel
+ System.clearProperty(PROPERTY_SKIP_STARTING_CAMEL_CONTEXT);
+
+ // route coverage need to know the test method
+ CamelAnnotationsHandler.handleRouteCoverage(context, testClass, s -> testName);
+
+ LOG.info("Initialized CamelSpringBootExecutionListener now ready to start CamelContext");
+ CamelAnnotationsHandler.handleCamelContextStartup(context, testClass);
+ }
+
+ @Override
+ public void afterTestMethod(TestContext testContext) throws Exception {
+ LOG.info("CamelSpringBootExecutionListener after: {}.{}", testContext.getTestClass(),
+ testContext.getTestMethod().getName());
+
+ Class<?> testClass = testContext.getTestClass();
+ String testName = testContext.getTestMethod().getName();
+
+ ConfigurableApplicationContext context = threadApplicationContext.get();
+ if (context != null && context.isRunning()) {
+ // dump route coverage for each test method so its accurate
+ // statistics
+ // even if spring application context is running (i.e. its not
+ // dirtied per test method)
+ CamelAnnotationsHandler.handleRouteCoverageDump(context, testClass, s -> testName);
+ }
+ }
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringBootTest.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringBootTest.java
new file mode 100644
index 0000000..bbe8e68
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringBootTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.spring.junit5;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.test.context.TestExecutionListeners;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE })
+@ExtendWith(SpringExtension.class)
+@TestExecutionListeners(
+ value = {
+ CamelSpringTestContextLoaderTestExecutionListener.class,
+ CamelSpringBootExecutionListener.class,
+ StopWatchTestExecutionListener.class
+ },
+ mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
+public @interface CamelSpringBootTest {
+
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTest.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTest.java
new file mode 100644
index 0000000..7ddfd4a
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.spring.junit5;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.test.context.BootstrapWith;
+import org.springframework.test.context.TestExecutionListeners;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE })
+@ExtendWith(SpringExtension.class)
+@BootstrapWith(CamelTestContextBootstrapper.class)
+@TestExecutionListeners(
+ value = {
+ CamelSpringTestContextLoaderTestExecutionListener.class,
+ StopWatchTestExecutionListener.class
+ },
+ mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
+public @interface CamelSpringTest {
+
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTestContextLoader.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTestContextLoader.java
new file mode 100644
index 0000000..3b5275a
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTestContextLoader.java
@@ -0,0 +1,241 @@
+/*
+ * 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.spring.junit5;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import org.apache.camel.spring.SpringCamelContext;
+import org.apache.camel.test.ExcludingPackageScanClassResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
+import org.springframework.context.annotation.AnnotationConfigUtils;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.test.context.MergedContextConfiguration;
+import org.springframework.test.context.support.AbstractContextLoader;
+import org.springframework.test.context.support.AbstractGenericContextLoader;
+import org.springframework.test.context.support.GenericXmlContextLoader;
+import org.springframework.util.StringUtils;
+
+/**
+ * Replacement for the default {@link GenericXmlContextLoader} that provides hooks for processing some class level Camel
+ * related test annotations.
+ */
+public class CamelSpringTestContextLoader extends AbstractContextLoader {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CamelSpringTestContextLoader.class);
+
+ /**
+ * Modeled after the Spring implementation in {@link AbstractGenericContextLoader}, this method creates and
+ * refreshes the application context while providing for processing of additional Camel specific post-refresh
+ * actions. We do not provide the pre-post hooks for customization seen in {@link AbstractGenericContextLoader}
+ * because they probably are unnecessary for 90+% of users.
+ * <p/>
+ * For some functionality, we cannot use {@link org.springframework.test.context.TestExecutionListener} because we
+ * need to both produce the desired outcome during application context loading, and also cleanup after ourselves
+ * even if the test class never executes. Thus the listeners, which only run if the application context is
+ * successfully initialized are insufficient to provide the behavior described above.
+ */
+ @Override
+ public ApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception {
+ Class<?> testClass = getTestClass();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Loading ApplicationContext for merged context configuration [{}].", mergedConfig);
+ }
+
+ try {
+ GenericApplicationContext context = createContext(testClass, mergedConfig);
+ prepareContext(context, mergedConfig);
+ loadBeanDefinitions(context, mergedConfig);
+ return loadContext(context, testClass);
+ } finally {
+ CamelAnnotationsHandler.cleanup(testClass);
+ }
+ }
+
+ /**
+ * Modeled after the Spring implementation in {@link AbstractGenericContextLoader}, this method creates and
+ * refreshes the application context while providing for processing of additional Camel specific post-refresh
+ * actions. We do not provide the pre-post hooks for customization seen in {@link AbstractGenericContextLoader}
+ * because they probably are unnecessary for 90+% of users.
+ * <p/>
+ * For some functionality, we cannot use {@link org.springframework.test.context.TestExecutionListener} because we
+ * need to both produce the desired outcome during application context loading, and also cleanup after ourselves
+ * even if the test class never executes. Thus the listeners, which only run if the application context is
+ * successfully initialized are insufficient to provide the behavior described above.
+ */
+ @Override
+ public ApplicationContext loadContext(String... locations) throws Exception {
+ Class<?> testClass = getTestClass();
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Loading ApplicationContext for locations [" + StringUtils.arrayToCommaDelimitedString(locations) + "].");
+ }
+
+ try {
+ GenericApplicationContext context = createContext(testClass, null);
+ loadBeanDefinitions(context, locations);
+ return loadContext(context, testClass);
+ } finally {
+ CamelAnnotationsHandler.cleanup(testClass);
+ }
+ }
+
+ /**
+ * Returns "<code>-context.xml</code>".
+ */
+ @Override
+ public String getResourceSuffix() {
+ return "-context.xml";
+ }
+
+ /**
+ * Performs the bulk of the Spring application context loading/customization.
+ *
+ * @param context the partially configured context. The context should have the bean definitions loaded, but
+ * nothing else.
+ * @param testClass the test class being executed
+ * @return the initialized (refreshed) Spring application context
+ *
+ * @throws Exception if there is an error during initialization/customization
+ */
+ protected ApplicationContext loadContext(GenericApplicationContext context, Class<?> testClass) throws Exception {
+
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+
+ // Pre CamelContext(s) instantiation setup
+ CamelAnnotationsHandler.handleDisableJmx(context, testClass);
+ CamelAnnotationsHandler.handleExcludeRoutes(context, testClass);
+ CamelAnnotationsHandler.handleUseOverridePropertiesWithPropertiesComponent(context, testClass);
+
+ // Temporarily disable CamelContext start while the contexts are instantiated.
+ SpringCamelContext.setNoStart(true);
+ context.refresh();
+ context.registerShutdownHook();
+ // Turn CamelContext startup back on since the context's have now been instantiated.
+ SpringCamelContext.setNoStart(false);
+
+ // Post CamelContext(s) instantiation but pre CamelContext(s) start setup
+ CamelAnnotationsHandler.handleRouteCoverage(context, testClass, s -> getTestMethod().getName());
+ CamelAnnotationsHandler.handleProvidesBreakpoint(context, testClass);
+ CamelAnnotationsHandler.handleShutdownTimeout(context, testClass);
+ CamelAnnotationsHandler.handleMockEndpoints(context, testClass);
+ CamelAnnotationsHandler.handleMockEndpointsAndSkip(context, testClass);
+
+ // CamelContext(s) startup
+ CamelAnnotationsHandler.handleCamelContextStartup(context, testClass);
+
+ return context;
+ }
+
+ protected void loadBeanDefinitions(GenericApplicationContext context, MergedContextConfiguration mergedConfig) {
+ new XmlBeanDefinitionReader(context).loadBeanDefinitions(mergedConfig.getLocations());
+ new AnnotatedBeanDefinitionReader(context).register(mergedConfig.getClasses());
+ }
+
+ protected void loadBeanDefinitions(GenericApplicationContext context, String... locations) {
+ new XmlBeanDefinitionReader(context).loadBeanDefinitions(locations);
+ }
+
+ /**
+ * Creates and starts the Spring context while optionally starting any loaded Camel contexts.
+ *
+ * @param testClass the test class that is being executed
+ * @return the loaded Spring context
+ */
+ protected GenericApplicationContext createContext(Class<?> testClass, MergedContextConfiguration mergedConfig) {
+ ApplicationContext parentContext = null;
+ GenericApplicationContext routeExcludingContext = null;
+
+ if (mergedConfig != null) {
+ parentContext = mergedConfig.getParentApplicationContext();
+ }
+
+ if (testClass.isAnnotationPresent(ExcludeRoutes.class)) {
+ Class<?>[] excludedClasses = testClass.getAnnotation(ExcludeRoutes.class).value();
+
+ if (excludedClasses.length > 0) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Setting up package scanning excluded classes as ExcludeRoutes "
+ + "annotation was found. Excluding [" + StringUtils.arrayToCommaDelimitedString(excludedClasses)
+ + "].");
+ }
+
+ if (parentContext == null) {
+ routeExcludingContext = new GenericApplicationContext();
+ } else {
+ routeExcludingContext = new GenericApplicationContext(parentContext);
+ }
+ routeExcludingContext.registerBeanDefinition("excludingResolver",
+ new RootBeanDefinition(ExcludingPackageScanClassResolver.class));
+ routeExcludingContext.refresh();
+
+ ExcludingPackageScanClassResolver excludingResolver
+ = routeExcludingContext.getBean("excludingResolver", ExcludingPackageScanClassResolver.class);
+ List<Class<?>> excluded = Arrays.asList(excludedClasses);
+ excludingResolver.setExcludedClasses(new HashSet<>(excluded));
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Not enabling package scanning excluded classes as ExcludeRoutes "
+ + "annotation was found but no classes were excluded.");
+ }
+ }
+ }
+
+ GenericApplicationContext context;
+
+ if (routeExcludingContext != null) {
+ context = new GenericApplicationContext(routeExcludingContext);
+ } else {
+ if (parentContext != null) {
+ context = new GenericApplicationContext(parentContext);
+ } else {
+ context = new GenericApplicationContext();
+ }
+ }
+
+ return context;
+ }
+
+ /**
+ * Returns the class under test in order to enable inspection of annotations while the Spring context is being
+ * created.
+ *
+ * @return the test class that is being executed
+ * @see CamelSpringTestHelper
+ */
+ protected Class<?> getTestClass() {
+ return CamelSpringTestHelper.getTestClass();
+ }
+
+ /**
+ * Returns the test method under test.
+ *
+ * @return the method that is being executed
+ * @see CamelSpringTestHelper
+ */
+ protected Method getTestMethod() {
+ return CamelSpringTestHelper.getTestMethod();
+ }
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTestContextLoaderTestExecutionListener.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTestContextLoaderTestExecutionListener.java
new file mode 100644
index 0000000..b3eef1d
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTestContextLoaderTestExecutionListener.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.spring.junit5;
+
+import org.springframework.test.context.TestContext;
+import org.springframework.test.context.support.AbstractTestExecutionListener;
+
+/**
+ * Helper for {@link CamelSpringTestContextLoader} that sets the test class state in {@link CamelSpringTestHelper}
+ * almost immediately before the loader initializes the Spring context.
+ * <p/>
+ * Implemented as a listener as the state can be set on a {@code ThreadLocal} and we are pretty sure that the same
+ * thread will be used to initialize the Spring context.
+ */
+public class CamelSpringTestContextLoaderTestExecutionListener extends AbstractTestExecutionListener {
+
+ /**
+ * Returns the precedence that is used by Spring to choose the appropriate execution order of test listeners.
+ *
+ * See {@link SpringTestExecutionListenerSorter#getPrecedence(Class)} for more.
+ */
+ @Override
+ public int getOrder() {
+ return SpringTestExecutionListenerSorter.getPrecedence(getClass());
+ }
+
+ @Override
+ public void prepareTestInstance(TestContext testContext) throws Exception {
+ CamelSpringTestHelper.setTestClass(testContext.getTestClass());
+ CamelSpringTestHelper.setTestContext(testContext);
+ }
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTestHelper.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTestHelper.java
new file mode 100644
index 0000000..2f886c1
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTestHelper.java
@@ -0,0 +1,116 @@
+/*
+ * 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.spring.junit5;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.camel.spring.SpringCamelContext;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.TestContext;
+
+/**
+ * Helper that provides state information across the levels of Spring Test that do not expose the necessary
+ * context/state for integration of Camel testing features into Spring test. Also provides utility methods.
+ * <p/>
+ * Note that this class makes use of {@link ThreadLocal}s to maintain some state. It is imperative that the state
+ * setters and getters are accessed within the scope of a single thread in order for this class to work right.
+ */
+public final class CamelSpringTestHelper {
+
+ private static ThreadLocal<String> originalJmxDisabledValue = new ThreadLocal<>();
+ private static ThreadLocal<String> originalExcludeRoutesValue = new ThreadLocal<>();
+ private static ThreadLocal<Class<?>> testClazz = new ThreadLocal<>();
+ private static ThreadLocal<TestContext> testContext = new ThreadLocal<>();
+
+ private CamelSpringTestHelper() {
+ }
+
+ public static String getOriginalJmxDisabled() {
+ return originalJmxDisabledValue.get();
+ }
+
+ public static void setOriginalJmxDisabledValue(String originalValue) {
+ originalJmxDisabledValue.set(originalValue);
+ }
+
+ public static String getOriginalExcludeRoutes() {
+ return originalExcludeRoutesValue.get();
+ }
+
+ public static void setOriginalExcludeRoutesValue(String originalValue) {
+ originalExcludeRoutesValue.set(originalValue);
+ }
+
+ public static Class<?> getTestClass() {
+ return testClazz.get();
+ }
+
+ public static void setTestClass(Class<?> testClass) {
+ testClazz.set(testClass);
+ }
+
+ public static Method getTestMethod() {
+ return testContext.get().getTestMethod();
+ }
+
+ public static void setTestContext(TestContext context) {
+ testContext.set(context);
+ }
+
+ /**
+ * Returns all methods defined in {@code clazz} and its superclasses/interfaces.
+ */
+ public static Collection<Method> getAllMethods(Class<?> clazz) {
+ Set<Method> methods = new LinkedHashSet<>();
+ Class<?> currentClass = clazz;
+
+ while (currentClass != null) {
+ methods.addAll(Arrays.asList(clazz.getMethods()));
+ currentClass = currentClass.getSuperclass();
+ }
+
+ return methods;
+ }
+
+ /**
+ * Executes {@code strategy} against all {@link SpringCamelContext}s found in the Spring context. This method
+ * reduces the amount of repeated find and loop code throughout this class.
+ *
+ * @param context the Spring context to search
+ * @param strategy the strategy to execute against the found {@link SpringCamelContext}s
+ *
+ * @throws Exception if there is an error executing any of the strategies
+ */
+ public static void doToSpringCamelContexts(ApplicationContext context, DoToSpringCamelContextsStrategy strategy)
+ throws Exception {
+ Map<String, SpringCamelContext> contexts = context.getBeansOfType(SpringCamelContext.class);
+
+ for (Entry<String, SpringCamelContext> entry : contexts.entrySet()) {
+ strategy.execute(entry.getKey(), entry.getValue());
+ }
+ }
+
+ public interface DoToSpringCamelContextsStrategy {
+ void execute(String contextName, SpringCamelContext camelContext) throws Exception;
+ }
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTestSupport.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTestSupport.java
new file mode 100644
index 0000000..f1e10e64
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelSpringTestSupport.java
@@ -0,0 +1,221 @@
+/*
+ * 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.spring.junit5;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spring.SpringCamelContext;
+import org.apache.camel.test.ExcludingPackageScanClassResolver;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.ObjectHelper;
+import org.junit.jupiter.api.AfterEach;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.AbstractApplicationContext;
+import org.springframework.context.support.GenericApplicationContext;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Base test-class for classic Spring application such as standalone, web applications. Do <tt>not</tt> use this class
+ * for Spring Boot testing, instead use <code>@CamelSpringBootTest</code>.
+ */
+public abstract class CamelSpringTestSupport extends CamelTestSupport {
+
+ protected static ThreadLocal<AbstractApplicationContext> threadAppContext = new ThreadLocal<>();
+ protected static Object lock = new Object();
+
+ private static final Logger LOG = LoggerFactory.getLogger(CamelSpringTestSupport.class);
+
+ protected AbstractApplicationContext applicationContext;
+
+ protected abstract AbstractApplicationContext createApplicationContext();
+
+ @Override
+ public void postProcessTest() throws Exception {
+ if (isCreateCamelContextPerClass()) {
+ applicationContext = threadAppContext.get();
+ }
+ super.postProcessTest();
+ }
+
+ @Override
+ public void doPreSetup() throws Exception {
+ if (!"true".equalsIgnoreCase(System.getProperty("skipStartingCamelContext"))) {
+ // tell camel-spring it should not trigger starting CamelContext, since we do that later
+ // after we are finished setting up the unit test
+ synchronized (lock) {
+ SpringCamelContext.setNoStart(true);
+ if (isCreateCamelContextPerClass()) {
+ applicationContext = threadAppContext.get();
+ if (applicationContext == null) {
+ applicationContext = doCreateApplicationContext();
+ threadAppContext.set(applicationContext);
+ }
+ } else {
+ applicationContext = doCreateApplicationContext();
+ }
+ SpringCamelContext.setNoStart(false);
+ }
+ } else {
+ LOG.info("Skipping starting CamelContext as system property skipStartingCamelContext is set to be true.");
+ }
+ }
+
+ private AbstractApplicationContext doCreateApplicationContext() {
+ AbstractApplicationContext context = createApplicationContext();
+ assertNotNull(context, "Should have created a valid Spring application context");
+
+ String[] profiles = activeProfiles();
+ if (profiles != null && profiles.length > 0) {
+ // the context must not be active
+ if (context.isActive()) {
+ throw new IllegalStateException(
+ "Cannot active profiles: " + Arrays.asList(profiles) + " on active Spring application context: "
+ + context
+ + ". The code in your createApplicationContext() method should be adjusted to create the application context with refresh = false as parameter");
+ }
+ LOG.info("Spring activating profiles: {}", Arrays.asList(profiles));
+ context.getEnvironment().setActiveProfiles(profiles);
+ }
+
+ // ensure the context has been refreshed at least once
+ if (!context.isActive()) {
+ context.refresh();
+ }
+
+ return context;
+ }
+
+ @Override
+ @AfterEach
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ if (!isCreateCamelContextPerClass()) {
+ IOHelper.close(applicationContext);
+ applicationContext = null;
+ }
+ }
+
+ @Override
+ public void doPostTearDown() throws Exception {
+ super.doPostTearDown();
+
+ if (threadAppContext.get() != null) {
+ IOHelper.close(threadAppContext.get());
+ threadAppContext.remove();
+ }
+ }
+
+ /**
+ * Create a parent context that initializes a {@link org.apache.camel.spi.PackageScanClassResolver} to exclude a set
+ * of given classes from being resolved. Typically this is used at test time to exclude certain routes, which might
+ * otherwise be just noisy, from being discovered and initialized.
+ * <p/>
+ * To use this filtering mechanism it is necessary to provide the
+ * {@link org.springframework.context.ApplicationContext} returned from here as the parent context to your test
+ * context e.g.
+ *
+ * <pre>
+ * protected AbstractXmlApplicationContext createApplicationContext() {
+ * return new ClassPathXmlApplicationContext(
+ * new String[] { "test-context.xml" }, getRouteExcludingApplicationContext());
+ * }
+ * </pre>
+ *
+ * This will, in turn, call the template methods <code>excludedRoutes</code> and <code>excludedRoute</code> to
+ * determine the classes to be excluded from scanning.
+ *
+ * @return ApplicationContext a parent {@link org.springframework.context.ApplicationContext} configured to exclude
+ * certain classes from package scanning
+ */
+ protected ApplicationContext getRouteExcludingApplicationContext() {
+ GenericApplicationContext routeExcludingContext = new GenericApplicationContext();
+ routeExcludingContext.registerBeanDefinition("excludingResolver",
+ new RootBeanDefinition(ExcludingPackageScanClassResolver.class));
+ routeExcludingContext.refresh();
+
+ ExcludingPackageScanClassResolver excludingResolver
+ = routeExcludingContext.getBean("excludingResolver", ExcludingPackageScanClassResolver.class);
+ List<Class<?>> excluded = Arrays.asList(excludeRoutes());
+ excludingResolver.setExcludedClasses(new HashSet<>(excluded));
+
+ return routeExcludingContext;
+ }
+
+ /**
+ * Template method used to exclude {@link org.apache.camel.Route} from the test time context route scanning
+ *
+ * @return Class[] the classes to be excluded from test time context route scanning
+ */
+ protected Class<?>[] excludeRoutes() {
+ Class<?> excludedRoute = excludeRoute();
+ return excludedRoute != null ? new Class[] { excludedRoute } : new Class[0];
+ }
+
+ /**
+ * Template method used to exclude a {@link org.apache.camel.Route} from the test camel context
+ */
+ protected Class<?> excludeRoute() {
+ return null;
+ }
+
+ /**
+ * Looks up the mandatory spring bean of the given name and type, failing if it is not present or the correct type
+ */
+ public <T> T getMandatoryBean(Class<T> type, String name) {
+ Object value = applicationContext.getBean(name);
+ assertNotNull(value, "No spring bean found for name <" + name + ">");
+ if (type.isInstance(value)) {
+ return type.cast(value);
+ } else {
+ fail("Spring bean <" + name + "> is not an instanceof " + type.getName() + " but is of type "
+ + ObjectHelper.className(value));
+ return null;
+ }
+ }
+
+ /**
+ * Which active profiles should be used.
+ * <p/>
+ * <b>Important:</b> When using active profiles, then the code in {@link #createApplicationContext()} should create
+ * the Spring {@link org.springframework.context.support.AbstractApplicationContext} without refreshing. For example
+ * creating an {@link org.springframework.context.support.ClassPathXmlApplicationContext} you would need to pass in
+ * <tt>false</tt> in the refresh parameter, in the constructor. Camel will thrown an {@link IllegalStateException}
+ * if this is not correct stating this problem. The reason is that we cannot active profiles <b>after</b> a Spring
+ * application context has already been refreshed, and is active.
+ *
+ * @return an array of active profiles to use, use <tt>null</tt> to not use any active profiles.
+ */
+ protected String[] activeProfiles() {
+ return null;
+ }
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ // don't start the springCamelContext if we
+ return SpringCamelContext.springCamelContext(applicationContext, false);
+ }
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelTestContextBootstrapper.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelTestContextBootstrapper.java
new file mode 100644
index 0000000..6120d02
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/CamelTestContextBootstrapper.java
@@ -0,0 +1,31 @@
+/*
+ * 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.spring.junit5;
+
+import org.springframework.test.context.ContextLoader;
+import org.springframework.test.context.support.DefaultTestContextBootstrapper;
+
+/**
+ * To bootstrap Camel for testing with Spring 4.1 onwards.
+ */
+public class CamelTestContextBootstrapper extends DefaultTestContextBootstrapper {
+
+ @Override
+ protected Class<? extends ContextLoader> getDefaultContextLoaderClass(Class<?> testClass) {
+ return CamelSpringTestContextLoader.class;
+ }
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/DisableJmx.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/DisableJmx.java
new file mode 100644
index 0000000..0ea3d9b
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/DisableJmx.java
@@ -0,0 +1,43 @@
+/*
+ * 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.spring.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;
+
+/**
+ * Indicates if JMX should be globally disabled in the {@code CamelContext}s that are bootstrapped during the test
+ * through the use of Spring Test loaded application contexts. Note that the presence of this annotation will result in
+ * the manipulation of System Properties that will affect Camel contexts constructed outside of the Spring Test loaded
+ * application contexts.
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE })
+public @interface DisableJmx {
+
+ /**
+ * Whether the test annotated with this annotation should be run with JMX disabled in Camel. Defaults to
+ * {@code true}.
+ */
+ boolean value() default true;
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/EnableRouteCoverage.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/EnableRouteCoverage.java
new file mode 100644
index 0000000..8222427
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/EnableRouteCoverage.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.spring.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;
+
+/**
+ * Enables dumping route coverage statistic. The route coverage status is written as xml files in the
+ * <tt>target/camel-route-coverage</tt> directory after the test has finished.
+ * <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>.
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE })
+public @interface EnableRouteCoverage {
+
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/ExcludeRoutes.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/ExcludeRoutes.java
new file mode 100644
index 0000000..69916e4
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/ExcludeRoutes.java
@@ -0,0 +1,44 @@
+/*
+ * 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.spring.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.RoutesBuilder;
+
+/**
+ * Indicates if certain route builder classes should be excluded from discovery. Initializes a
+ * {@link org.apache.camel.spi.PackageScanClassResolver} to exclude a set of given classes from being resolved.
+ * Typically this is used at test time to exclude certain routes, which might otherwise be noisy, from being discovered
+ * and initialized.
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE })
+public @interface ExcludeRoutes {
+
+ /**
+ * The classes to exclude from resolution when using package scanning.
+ */
+ Class<? extends RoutesBuilder>[] value() default {};
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/MockEndpoints.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/MockEndpoints.java
new file mode 100644
index 0000000..0d95e32
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/MockEndpoints.java
@@ -0,0 +1,43 @@
+/*
+ * 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.spring.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.component.mock.InterceptSendToMockEndpointStrategy;
+
+/**
+ * Triggers the auto-mocking of endpoints whose URIs match the provided filter. The default filter is "*" which matches
+ * all endpoints. See {@link InterceptSendToMockEndpointStrategy} for more details on the registration of the mock
+ * endpoints.
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE })
+public @interface MockEndpoints {
+
+ /**
+ * The pattern to use for matching endpoints to enable mocking on.
+ */
+ String value() default "*";
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/MockEndpointsAndSkip.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/MockEndpointsAndSkip.java
new file mode 100644
index 0000000..8abb2b1
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/MockEndpointsAndSkip.java
@@ -0,0 +1,43 @@
+/*
+ * 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.spring.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.component.mock.InterceptSendToMockEndpointStrategy;
+
+/**
+ * Triggers the auto-mocking of endpoints whose URIs match the provided filter with the added provision that the
+ * endpoints are also skipped. The default filter is "*" which matches all endpoints. See
+ * {@link InterceptSendToMockEndpointStrategy} for more details on the registration of the mock endpoints.
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE })
+public @interface MockEndpointsAndSkip {
+
+ /**
+ * The pattern to use for matching endpoints to enable mocking on.
+ */
+ String value() default "*";
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/ProvidesBreakpoint.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/ProvidesBreakpoint.java
new file mode 100644
index 0000000..0f11c8d
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/ProvidesBreakpoint.java
@@ -0,0 +1,36 @@
+/*
+ * 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.spring.junit5;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.camel.spi.Breakpoint;
+
+/**
+ * Indicates that the annotated method returns a {@link Breakpoint} for use in the test. Useful for intercepting traffic
+ * to all endpoints or simply for setting a break point in an IDE for debugging. The method must be {@code public},
+ * {@code static}, take no arguments, and return {@link Breakpoint}.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+public @interface ProvidesBreakpoint {
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/RouteCoverageDumper.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/RouteCoverageDumper.java
new file mode 100644
index 0000000..e664a3a
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/RouteCoverageDumper.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.spring.junit5;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.api.management.ManagedCamelContext;
+import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
+import org.apache.camel.util.IOHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper to dump route coverage when using {@link EnableRouteCoverage}.
+ */
+public final class RouteCoverageDumper {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RouteCoverageDumper.class);
+
+ private RouteCoverageDumper() {
+ }
+
+ public static void dumpRouteCoverage(CamelContext context, String testClassName, String testName) {
+ try {
+ String dir = "target/camel-route-coverage";
+ String name = testClassName + "-" + testName + ".xml";
+
+ ManagedCamelContextMBean managedCamelContext
+ = context.getExtension(ManagedCamelContext.class).getManagedCamelContext();
+ if (managedCamelContext == null) {
+ LOG.warn(
+ "Cannot dump route coverage to file as JMX is not enabled. Override useJmx() method to enable JMX in the unit test classes.");
+ } else {
+ String xml = managedCamelContext.dumpRoutesCoverageAsXml();
+ String combined = "<camelRouteCoverage>\n" + gatherTestDetailsAsXml(testClassName, testName) + xml
+ + "\n</camelRouteCoverage>";
+
+ File file = new File(dir);
+ // ensure dir exists
+ file.mkdirs();
+ file = new File(dir, name);
+
+ LOG.info("Dumping route coverage to file: {}", file);
+ InputStream is = new ByteArrayInputStream(combined.getBytes());
+ OutputStream os = new FileOutputStream(file, false);
+ IOHelper.copyAndCloseInput(is, os);
+ IOHelper.close(os);
+ }
+ } catch (Exception e) {
+ LOG.warn("Error during dumping route coverage statistic. This exception is ignored.", e);
+ }
+
+ }
+
+ /**
+ * Gathers test details as xml
+ */
+ private static String gatherTestDetailsAsXml(String testClassName, String testName) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("<test>\n");
+ sb.append(" <class>").append(testClassName).append("</class>\n");
+ sb.append(" <method>").append(testName).append("</method>\n");
+ sb.append("</test>\n");
+ return sb.toString();
+ }
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/RouteCoverageEventNotifier.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/RouteCoverageEventNotifier.java
new file mode 100644
index 0000000..3642323
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/RouteCoverageEventNotifier.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.spring.junit5;
+
+import java.util.function.Function;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.CamelEvent;
+import org.apache.camel.spi.CamelEvent.CamelContextStoppingEvent;
+import org.apache.camel.support.EventNotifierSupport;
+
+public class RouteCoverageEventNotifier extends EventNotifierSupport {
+
+ private final String testClassName;
+ private final Function testMethodName;
+
+ public RouteCoverageEventNotifier(String testClassName, Function testMethodName) {
+ this.testClassName = testClassName;
+ this.testMethodName = testMethodName;
+ setIgnoreCamelContextEvents(false);
+ setIgnoreExchangeEvents(true);
+ }
+
+ @Override
+ public boolean isEnabled(CamelEvent event) {
+ return event instanceof CamelContextStoppingEvent;
+ }
+
+ @Override
+ public void notify(CamelEvent event) throws Exception {
+ CamelContext context = ((CamelContextStoppingEvent) event).getContext();
+ String testName = (String) testMethodName.apply(this);
+ RouteCoverageDumper.dumpRouteCoverage(context, testClassName, testName);
+ }
+
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/ShutdownTimeout.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/ShutdownTimeout.java
new file mode 100644
index 0000000..4db7a12
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/ShutdownTimeout.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.spring.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 java.util.concurrent.TimeUnit;
+
+/**
+ * Indicates to set the shutdown timeout of all {@code CamelContext}s instantiated through the use of Spring Test loaded
+ * application contexts. If no annotation is used, the timeout is automatically reduced to 10 seconds by the test
+ * framework. If the annotation is present the shutdown timeout is set based on the value of {@link #value()}.
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE })
+public @interface ShutdownTimeout {
+
+ /**
+ * The shutdown timeout to set on the {@code CamelContext}(s). Defaults to {@code 10} seconds.
+ */
+ int value() default 10;
+
+ /**
+ * The time unit that {@link #value()} is in.
+ */
+ TimeUnit timeUnit() default TimeUnit.SECONDS;
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/SpringTestExecutionListenerSorter.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/SpringTestExecutionListenerSorter.java
new file mode 100644
index 0000000..fae17fd
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/SpringTestExecutionListenerSorter.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.spring.junit5;
+
+import org.springframework.core.Ordered;
+
+/**
+ * This class centralizes the order of execution of spring test execution listeners:
+ * <ol>
+ * <li>{@link CamelSpringTestContextLoaderTestExecutionListener}</li>
+ * <li>{@link StopWatchTestExecutionListener}</li>
+ * <li>Spring default listeners</li>
+ * </ol>
+ */
+public final class SpringTestExecutionListenerSorter {
+
+ private SpringTestExecutionListenerSorter() {
+ }
+
+ public static int getPrecedence(Class<?> clazz) {
+ if (clazz == StopWatchTestExecutionListener.class) {
+ return Ordered.HIGHEST_PRECEDENCE + 4000;
+ } else if (clazz == CamelSpringBootExecutionListener.class) {
+ return Ordered.HIGHEST_PRECEDENCE + 3000;
+ } else if (clazz == CamelSpringTestContextLoaderTestExecutionListener.class) {
+ return Ordered.HIGHEST_PRECEDENCE + 1000;
+ }
+ throw new IllegalArgumentException("Impossible to get the precedence of the class " + clazz.getName() + ".");
+ }
+
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/StopWatchTestExecutionListener.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/StopWatchTestExecutionListener.java
new file mode 100644
index 0000000..f83a556
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/StopWatchTestExecutionListener.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.spring.junit5;
+
+import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.TimeUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.TestContext;
+import org.springframework.test.context.support.AbstractTestExecutionListener;
+
+/**
+ * An execution listener that simulates the timing output built in to
+ * {@link org.apache.camel.test.junit5.CamelTestSupport}.
+ */
+public class StopWatchTestExecutionListener extends AbstractTestExecutionListener {
+
+ protected static ThreadLocal<StopWatch> threadStopWatch = new ThreadLocal<>();
+
+ /**
+ * Returns the precedence that is used by Spring to choose the appropriate execution order of test listeners.
+ *
+ * See {@link SpringTestExecutionListenerSorter#getPrecedence(Class)} for more.
+ */
+ @Override
+ public int getOrder() {
+ return SpringTestExecutionListenerSorter.getPrecedence(getClass());
+ }
+
+ /**
+ * Exists primarily for testing purposes, but allows for access to the underlying stop watch instance for a test.
+ */
+ public static StopWatch getStopWatch() {
+ return threadStopWatch.get();
+ }
+
+ @Override
+ public void beforeTestMethod(TestContext testContext) throws Exception {
+ StopWatch stopWatch = new StopWatch();
+ threadStopWatch.set(stopWatch);
+ }
+
+ @Override
+ public void afterTestMethod(TestContext testContext) throws Exception {
+ StopWatch watch = threadStopWatch.get();
+ if (watch != null) {
+ long time = watch.taken();
+ Logger log = LoggerFactory.getLogger(testContext.getTestClass());
+
+ log.info("********************************************************************************");
+ log.info("Testing done: {} ({})", testContext.getTestMethod().getName(), testContext.getTestClass().getName());
+ log.info("Took: {} ({} millis)", TimeUtils.printDuration(time), time);
+ log.info("********************************************************************************");
+
+ threadStopWatch.remove();
+ }
+ }
+
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/UseAdviceWith.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/UseAdviceWith.java
new file mode 100644
index 0000000..160d6c4
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/UseAdviceWith.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.test.spring.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.CamelContext;
+
+/**
+ * Indicates the use of {@code adviceWith()} within the test class. If a class is annotated with this annotation and
+ * {@link UseAdviceWith#value()} returns true, any {@code CamelContext}s bootstrapped during the test through the use of
+ * Spring Test loaded application contexts will not be started automatically. The test author is responsible for
+ * injecting the Camel contexts into the test and executing {@link CamelContext#start()} on them at the appropriate time
+ * after any advice has been applied to the routes in the Camel context(s).
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE })
+public @interface UseAdviceWith {
+
+ /**
+ * Whether the test annotated with this annotation should be treated as if {@code adviceWith()} is in use in the
+ * test and the Camel contexts should not be started automatically. Defaults to {@code true}.
+ */
+ boolean value() default true;
+}
diff --git a/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/UseOverridePropertiesWithPropertiesComponent.java b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/UseOverridePropertiesWithPropertiesComponent.java
new file mode 100644
index 0000000..bd2cf30
--- /dev/null
+++ b/components/camel-test/camel-test-spring-junit5/src/main/java/org/apache/camel/test/spring/junit5/UseOverridePropertiesWithPropertiesComponent.java
@@ -0,0 +1,35 @@
+/*
+ * 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.spring.junit5;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
... 15426 lines suppressed ...