This is an automated email from the ASF dual-hosted git repository.
jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/main by this push:
new 0b7564fa37 Improve AdviceWith testing with CamelQuarkusTestSupport
0b7564fa37 is described below
commit 0b7564fa37cab2b8ebf62575560ff7834d88b2f9
Author: James Netherton <[email protected]>
AuthorDate: Fri May 22 07:13:04 2026 +0100
Improve AdviceWith testing with CamelQuarkusTestSupport
Fixes #4927
Fixes #7140
Fixes #7264
This commit addresses user-reported issues with AdviceWith where:
- Kafka consumers might not be fully initialized when tests run
- Tests fail on first run but succeed on subsequent runs
- Advice from one test persists and interferes with subsequent tests
Key improvements:
1. Automatic route starting after advice
- Routes that aren't advised are now automatically started after
@BeforeEach or doBeforeEach() completes
- No need to manually call startRouteDefinitions() anymore
- Ensures proper consumer initialization timing
2. Automatic advice cleanup between tests
- Route definitions are automatically restored to their original state
between test methods
- Prevents advice interference between tests
- Each test starts with a clean route state
3. New adviceRoute() helper method
- Simplifies per-test-method advice by handling lifecycle automatically
(stop, advice, start)
- Makes it easy to apply different advice in different test methods
- Example: adviceRoute("my-route", route -> {...})
4. Comprehensive test coverage
- Added Kafka integration tests validating consumer initialization
- Added test-framework tests for fast CI validation
- Tests cover: BeforeEach pattern, per-method advice, cleanup
verification
5. Documentation improvements
- Expanded testing guide with AdviceWith patterns and best practices
- Created migration guide for 3.36.0 with before/after examples
- Clear guidance on when to use each approach
Tested with user-provided reproducers - both now pass successfully.
Co-authored-by: Claude Sonnet 4.5 <[email protected]>
The test/resources/application.properties was added to force test ordering
as a workaround. Now that route restoration is properly implemented, test
ordering no longer matters and this file is not needed.
---
.../modules/ROOT/pages/migration-guide/3.36.0.adoc | 63 +++++++
docs/modules/ROOT/pages/migration-guide/index.adoc | 1 +
docs/modules/ROOT/pages/user-guide/testing.adoc | 139 ++++++++++++--
integration-tests/kafka/pom.xml | 5 +
.../quarkus/component/kafka/CamelKafkaRoutes.java | 26 +++
.../src/main/resources/application.properties | 3 +
.../kafka/it/KafkaAdviceRouteInBeforeEachTest.java | 86 +++++++++
.../component/kafka/it/KafkaAdviceWithTest.java | 108 +++++++++++
.../kafka/it/KafkaPerTestAdviceExample.java | 113 +++++++++++
.../apache/camel/quarkus/test/CallbackUtil.java | 21 ++-
.../quarkus/test/CamelQuarkusTestSupport.java | 206 ++++++++++++++++++++-
.../test/junit6/AdviceRouteInDoBeforeEachTest.java | 103 +++++++++++
.../test/junit6/AdviceRouteUtilityTest.java | 143 ++++++++++++++
.../test/junit6/AdviceWithBeforeEachTest.java | 138 ++++++++++++++
.../junit6/AutoStartRoutesAfterAdviceTest.java | 95 ++++++++++
.../quarkus/test/junit6/GlobalRouteAdviceTest.java | 92 +++++++++
.../test/junit6/GlobalRouteWeaveByIdTest.java | 82 ++++++++
17 files changed, 1398 insertions(+), 26 deletions(-)
diff --git a/docs/modules/ROOT/pages/migration-guide/3.36.0.adoc
b/docs/modules/ROOT/pages/migration-guide/3.36.0.adoc
new file mode 100644
index 0000000000..42b144cd30
--- /dev/null
+++ b/docs/modules/ROOT/pages/migration-guide/3.36.0.adoc
@@ -0,0 +1,63 @@
+= Camel Quarkus 3.36.0 Migration Guide
+
+The following guide outlines how to adapt your code to changes that were made
in Camel Quarkus 3.36.0.
+
+== CamelQuarkusTestSupport - AdviceWith improvements
+
+The testing framework has been significantly improved to make AdviceWith
easier to use and more reliable.
+
+=== Automatic route starting after advice
+
+Routes that are not advised are now automatically started after
`doBeforeEach()` or `@BeforeEach` completes. This means you no longer need to
manually call `startRouteDefinitions()` when using AdviceWith.
+
+**Before (3.35.0 and earlier):**
+[source,java]
+----
+@BeforeEach
+public void beforeEach() throws Exception {
+ AdviceWith.adviceWith(context, "my-route", route -> {
+ route.weaveByToUri("kafka:*").replace().to("mock:result");
+ });
+
+ // Required to start non-advised routes
+ startRouteDefinitions();
+}
+----
+
+**After (3.36.0):**
+[source,java]
+----
+@BeforeEach
+void beforeEach() throws Exception {
+ AdviceWith.adviceWith(this.context, "my-route", route -> {
+ route.weaveByToUri("kafka:*").replace().to("mock:result");
+ });
+
+ // startRouteDefinitions() is no longer needed - auto-start handles this
+}
+----
+
+=== New adviceRoute() helper method
+
+A new `adviceRoute()` helper method has been added to simplify per-test-method
advice. This method handles route lifecycle (stop, advice, start) automatically.
+
+**Example:**
+[source,java]
+----
+@Test
+void testWithDifferentAdvice() throws Exception {
+ adviceRoute("my-route", route -> {
+ route.weaveByToUri("kafka:*").replace().to("mock:stub");
+ });
+
+ // Test assertions...
+}
+----
+
+This is particularly useful when different tests in the same class need
different advice applied.
+
+=== Automatic advice cleanup
+
+Advice modifications are now automatically cleaned up between test methods to
prevent interference between tests. This ensures that each test starts with a
clean route state.
+
+For more details, see the
xref:user-guide/testing.adoc#_using_advicewith[Testing guide].
diff --git a/docs/modules/ROOT/pages/migration-guide/index.adoc
b/docs/modules/ROOT/pages/migration-guide/index.adoc
index 98a2883bf7..5c0025ea73 100644
--- a/docs/modules/ROOT/pages/migration-guide/index.adoc
+++ b/docs/modules/ROOT/pages/migration-guide/index.adoc
@@ -4,6 +4,7 @@ We do frequent releases, a release almost every month, and even
though we strive
Listed here are guides on how to migrate between major versions and anything
of significance to watch for when upgrading from minor versions.
+* xref:migration-guide/3.36.0.adoc[Camel Quarkus 3.35.x to Camel Quarkus
3.36.0 migration guide]
* xref:migration-guide/3.35.0.adoc[Camel Quarkus 3.33.x to Camel Quarkus
3.35.0 migration guide]
* xref:migration-guide/3.33.0.adoc[Camel Quarkus 3.32.x to Camel Quarkus
3.33.0 migration guide]
* xref:migration-guide/3.31.0.adoc[Camel Quarkus 3.30.x to Camel Quarkus
3.31.0 migration guide]
diff --git a/docs/modules/ROOT/pages/user-guide/testing.adoc
b/docs/modules/ROOT/pages/user-guide/testing.adoc
index b6b20b218e..490d2c3b37 100644
--- a/docs/modules/ROOT/pages/user-guide/testing.adoc
+++ b/docs/modules/ROOT/pages/user-guide/testing.adoc
@@ -381,27 +381,67 @@ class SimpleTest extends CamelQuarkusTestSupport {
==== Using `AdviceWith`
+xref:manual::advice-with.adoc[AdviceWith] allows you to modify routes at
runtime for testing purposes - replacing endpoints, adding interceptors, or
changing route behavior without modifying production code.
+
+===== Advising existing application routes
+
+The recommended pattern is:
+
+1. Define routes in your *application code* (src/main/java)
+2. Use `AdviceWith` to modify these *existing* routes in your tests
+
+[source,java]
+----
+// Application code: src/main/java/com/example/MyRoutes.java
+public class MyRoutes extends RouteBuilder {
+ @Override
+ public void configure() {
+ from("kafka:input-topic").routeId("my-kafka-route")
+ .to("kafka:output-topic")
+ .to("mock:result");
+ }
+}
+
+// Test code: src/test/java/com/example/MyRoutesTest.java
+@QuarkusTest
+class MyRoutesTest extends CamelQuarkusTestSupport {
+ @BeforeEach
+ public void beforeEach() throws Exception {
+ // Advise the application route
+ AdviceWith.adviceWith(this.context, "my-kafka-route", route -> {
+
route.weaveByToUri("kafka:output-topic*").replace().to("mock:kafka-stub");
+ });
+ }
+
+ @Test
+ void testRoute() throws Exception {
+ MockEndpoint stub = getMockEndpoint("mock:kafka-stub");
+ stub.expectedMessageCount(1);
+
+ template.sendBody("kafka:input-topic", "test");
+
+ stub.assertIsSatisfied();
+ }
+}
+----
+
+WARNING: Advising routes created via `createRouteBuilder()` in tests can lead
to unpredictable behavior.
+
+===== Apply advice in `@BeforeEach` or `doBeforeEach`
+
+When all tests in a class need the same advice, apply it in `@BeforeEach` or
override `doBeforeEach`:
+
[source,java]
----
@QuarkusTest
class SimpleTest extends CamelQuarkusTestSupport {
@BeforeEach
public void beforeEach() throws Exception {
+ // Apply advice to EXISTING application route
AdviceWith.adviceWith(this.context, "advisedRoute", route -> {
route.replaceFromWith("direct:replaced");
});
- }
-
- @Override
- protected RoutesBuilder createRouteBuilder() throws Exception {
- return new RouteBuilder() {
- @Override
- public void configure() throws Exception {
- from("direct:start").routeId("advisedRoute")
- .transform().simple("Hello ${body}")
- .to("mock:result");
- }
- };
+ // Routes that aren't advised are automatically started after this
method completes
}
@Test
@@ -416,10 +456,79 @@ class SimpleTest extends CamelQuarkusTestSupport {
}
----
-==== Explicitly enabling advice
+Routes that aren't advised are automatically started after `@BeforeEach` or
`doBeforeEach` completes. Advice modifications are automatically cleaned up
between test methods
+
+===== Per-test-method advice using `adviceRoute()`
+
+When you need *different* advice in each test method, use the `adviceRoute()`
helper method which handles route lifecycle (stop, advice, start) automatically:
+
+[source,java]
+----
+@QuarkusTest
+class PerMethodAdviceTest extends CamelQuarkusTestSupport {
+ @Test
+ void testWithMockOutput() throws Exception {
+ // Apply advice specific to this test on EXISTING application route
+ adviceRoute("my-route", route -> {
+
route.weaveByToUri("kafka:output*").replace().to("mock:kafka-stub");
+ });
+
+ MockEndpoint stub = getMockEndpoint("mock:kafka-stub");
+ stub.expectedMessageCount(1);
+
+ template.sendBody("kafka:input", "test message");
+
+ stub.assertIsSatisfied();
+ }
+
+ @Test
+ void testWithDifferentAdvice() throws Exception {
+ // Different advice for this test on the same EXISTING route
+ adviceRoute("my-route", route -> {
+ route.weaveByToUri("kafka:output*").replace()
+ .transform(simple("MODIFIED: ${body}"))
+ .to("mock:result");
+ });
+
+ // ... different test assertions
+ }
+}
+----
+
+===== Advice patterns
+
+IMPORTANT: When using `weaveByToUri()`, `weaveByType()`, or similar methods,
use specific patterns rather than wildcards to avoid unintentionally matching
endpoints from the main application or other routes.
+
+For example, instead of:
+[source,java]
+----
+// Matches ALL Kafka endpoints in the context
+route.weaveByToUri("kafka:*").replace().to("mock:result");
+----
+
+Use specific endpoint URIs:
+[source,java]
+----
+// Only matches the specific endpoint you intend to advise
+route.weaveByToUri("kafka:my-output-topic*").replace().to("mock:result");
+----
+
+Or use IDs for even more precision:
+[source,java]
+----
+// Uses ID for exact matching
+route.weaveById("kafkaProducer").replace().to("mock:result");
+----
+
+Wildcard patterns like `kafka:*`, `http:*`, or `direct:*` will match **all**
endpoints of that type throughout the entire `CamelContext`, including routes
from your main application that the test may not need to modify. This can cause
unexpected test failures and interference between tests.
+
+===== Advice cleanup between tests
+
+Advice modifications are automatically cleaned up between test methods to
prevent interference. This ensures that:
-When explicitly
xref:manual::advice-with.adoc#_enabling_advice_during_testing[enabling advice]
you must invoke `startRouteDefinitions` when completing your `AdviceWith` setup.
-Note that this is only required if you have routes configured that are NOT
being advised.
+* Each test starts with a clean route state
+* Advice from one test doesn't affect subsequent tests
+* Tests remain independent and can run in any order
=== Limitations
diff --git a/integration-tests/kafka/pom.xml b/integration-tests/kafka/pom.xml
index 1036f78474..7d93490aaa 100644
--- a/integration-tests/kafka/pom.xml
+++ b/integration-tests/kafka/pom.xml
@@ -73,6 +73,11 @@
</dependency>
<!-- test dependencies -->
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
diff --git
a/integration-tests/kafka/src/main/java/org/apache/camel/quarkus/component/kafka/CamelKafkaRoutes.java
b/integration-tests/kafka/src/main/java/org/apache/camel/quarkus/component/kafka/CamelKafkaRoutes.java
index b48ed8786d..0341e21c1d 100644
---
a/integration-tests/kafka/src/main/java/org/apache/camel/quarkus/component/kafka/CamelKafkaRoutes.java
+++
b/integration-tests/kafka/src/main/java/org/apache/camel/quarkus/component/kafka/CamelKafkaRoutes.java
@@ -106,5 +106,31 @@ public class CamelKafkaRoutes extends RouteBuilder {
from("kafka:test-propagation?headerDeserializer=#customHeaderDeserializer")
.to(SEDA_HEADER_PROPAGATION);
+ // AdviceWith Test Routes
+ // These routes are used by AdviceWith tests to demonstrate modifying
existing routes
+ // Tests advise these routes to replace Kafka producers with mocks for
testing
+
+ // Route 1: Primary route for AdviceWith testing
+
from("kafka:advice-test-topic-1?groupId=advice-test-group-1&autoOffsetReset=earliest")
+ .routeId("advice-test-route-1")
+ .log("Received on advice-test-route-1: ${body}")
+ .setBody(simple("Processed: ${body}"))
+ .to("kafka:advice-output-topic-1")
+ .to("mock:advice-result-1");
+
+ // Route 2: Secondary route for testing auto-start behavior
+
from("kafka:advice-test-topic-2?groupId=advice-test-group-2&autoOffsetReset=earliest")
+ .routeId("advice-test-route-2")
+ .log("Received on advice-test-route-2: ${body}")
+ .setBody(simple("Processed: ${body}"))
+ .to("mock:advice-result-2");
+
+ // Route 3: Route for per-test advice variations
+
from("kafka:per-test-advice-topic?groupId=per-test-advice-group&autoOffsetReset=earliest")
+ .routeId("per-test-advice-route")
+ .log("Received on per-test-advice-route: ${body}")
+ .setBody(simple("Processed: ${body}"))
+ .to("kafka:per-test-output-topic");
+
}
}
diff --git a/integration-tests/kafka/src/main/resources/application.properties
b/integration-tests/kafka/src/main/resources/application.properties
index 5407f65f94..7c57a45b57 100644
--- a/integration-tests/kafka/src/main/resources/application.properties
+++ b/integration-tests/kafka/src/main/resources/application.properties
@@ -27,3 +27,6 @@ quarkus.log.category."org.apache.kafka".level = WARNING
%quiet.quarkus.log.category."kafka.log".level = FATAL
quarkus.kafka.devservices.enabled=false
+
+# To ensure proper graceful shutdown of Kafka routes
+camel.main.shutdownTimeout=120
diff --git
a/integration-tests/kafka/src/test/java/org/apache/camel/quarkus/component/kafka/it/KafkaAdviceRouteInBeforeEachTest.java
b/integration-tests/kafka/src/test/java/org/apache/camel/quarkus/component/kafka/it/KafkaAdviceRouteInBeforeEachTest.java
new file mode 100644
index 0000000000..4decebccd0
--- /dev/null
+++
b/integration-tests/kafka/src/test/java/org/apache/camel/quarkus/component/kafka/it/KafkaAdviceRouteInBeforeEachTest.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.kafka.it;
+
+import java.util.UUID;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.quarkus.test.CamelQuarkusTestSupport;
+import org.apache.camel.quarkus.test.support.kafka.KafkaTestResource;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test that verifies using adviceRoute() in doBeforeEach() works correctly.
+ * <p>
+ * This test uses the {@code adviceRoute()} utility method to advise
+ * EXISTING routes (defined in CamelKafkaRoutes) rather than creating new test
routes.
+ * <p>
+ * The {@code adviceRoute()} method handles the stop/advise/start lifecycle
automatically,
+ * making it convenient for use in doBeforeEach().
+ */
+@QuarkusTest
+@QuarkusTestResource(KafkaTestResource.class)
+public class KafkaAdviceRouteInBeforeEachTest extends CamelQuarkusTestSupport {
+
+ private static final String TOPIC_ADVISED = "advice-test-topic-1";
+ private static final String TOPIC_NOT_ADVISED = "advice-test-topic-2";
+
+ @Override
+ protected void doBeforeEach(QuarkusTestMethodContext context) throws
Exception {
+ // Use adviceRoute() to advise an EXISTING route (from
CamelKafkaRoutes.java)
+ // The adviceRoute() utility handles stop/advise/start automatically
+ adviceRoute("advice-test-route-1", route -> {
+ // Use specific URI to avoid matching other Kafka endpoints
+
route.weaveByToUri("kafka:advice-output-topic-1*").replace().to("mock:kafka-advised");
+ });
+ }
+
+ @Test
+ void testAdvisedRoute() throws Exception {
+ MockEndpoint kafkaStub = getMockEndpoint("mock:kafka-advised");
+ MockEndpoint result = getMockEndpoint("mock:advice-result-1");
+
+ String messageBody = "Test message " + UUID.randomUUID();
+
+ kafkaStub.expectedMessageCount(1);
+ kafkaStub.expectedBodiesReceived("Processed: " + messageBody);
+ result.expectedMessageCount(1);
+
+ template.sendBody("kafka:" + TOPIC_ADVISED, messageBody);
+
+ kafkaStub.assertIsSatisfied(10000);
+ result.assertIsSatisfied(10000);
+ }
+
+ @Test
+ void testNonAdvisedRouteStillWorks() throws Exception {
+ // Verifies that advice-test-route-2 continues working even though we
advised route-1
+ MockEndpoint result = getMockEndpoint("mock:advice-result-2");
+
+ String messageBody = "Test message " + UUID.randomUUID();
+
+ result.expectedMessageCount(1);
+ result.expectedBodiesReceived("Processed: " + messageBody);
+
+ template.sendBody("kafka:" + TOPIC_NOT_ADVISED, messageBody);
+
+ result.assertIsSatisfied(10000);
+ }
+}
diff --git
a/integration-tests/kafka/src/test/java/org/apache/camel/quarkus/component/kafka/it/KafkaAdviceWithTest.java
b/integration-tests/kafka/src/test/java/org/apache/camel/quarkus/component/kafka/it/KafkaAdviceWithTest.java
new file mode 100644
index 0000000000..260ae3a053
--- /dev/null
+++
b/integration-tests/kafka/src/test/java/org/apache/camel/quarkus/component/kafka/it/KafkaAdviceWithTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.quarkus.component.kafka.it;
+
+import java.util.UUID;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.quarkus.test.CamelQuarkusTestSupport;
+import org.apache.camel.quarkus.test.support.kafka.KafkaTestResource;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Demonstrates AdviceWith pattern with existing Kafka routes.
+ * <p>
+ * This test advises EXISTING routes (defined in CamelKafkaRoutes)
+ * rather than creating new test routes. This avoids CamelContext state
corruption.
+ */
+@QuarkusTest
+@QuarkusTestResource(KafkaTestResource.class)
+public class KafkaAdviceWithTest extends CamelQuarkusTestSupport {
+
+ private static final String TOPIC_ADVISED = "advice-test-topic-1";
+ private static final String TOPIC_NOT_ADVISED = "advice-test-topic-2";
+
+ @BeforeEach
+ void beforeEach() throws Exception {
+ // Advise an EXISTING route (defined in CamelKafkaRoutes.java)
+ AdviceWith.adviceWith(this.context, "advice-test-route-1", route -> {
+ // Replace the specific Kafka producer with a mock
+ // Use exact URI instead of wildcard to avoid matching other Kafka
endpoints
+
route.weaveByToUri("kafka:advice-output-topic-1*").replace().to("mock:kafka-advised");
+ });
+ }
+
+ @Test
+ void testAdvisedKafkaRoute() throws Exception {
+ // The Kafka producer was replaced with mock:kafka-advised by the
advice
+ MockEndpoint kafkaStub = getMockEndpoint("mock:kafka-advised");
+ // The route also has a built-in mock endpoint
+ MockEndpoint result = getMockEndpoint("mock:advice-result-1");
+
+ String messageBody = "Test message " + UUID.randomUUID();
+
+ kafkaStub.expectedMessageCount(1);
+ kafkaStub.expectedBodiesReceived("Processed: " + messageBody);
+ result.expectedMessageCount(1);
+
+ // Send message to Kafka topic
+ template.sendBody("kafka:" + TOPIC_ADVISED, messageBody);
+
+ // Verify the message was received and processed
+ kafkaStub.assertIsSatisfied(10000);
+ result.assertIsSatisfied(10000);
+ }
+
+ @Test
+ void testNonAdvisedKafkaRouteStillWorks() throws Exception {
+ // This verifies that advice-test-route-2 continues to work even
though we advised route-1
+ MockEndpoint result = getMockEndpoint("mock:advice-result-2");
+
+ String messageBody = "Test message " + UUID.randomUUID();
+
+ result.expectedMessageCount(1);
+ result.expectedBodiesReceived("Processed: " + messageBody);
+
+ // Send message to Kafka topic
+ template.sendBody("kafka:" + TOPIC_NOT_ADVISED, messageBody);
+
+ // Verify the message was received and processed
+ result.assertIsSatisfied(10000);
+ }
+
+ @Test
+ void testMultipleMessagesOnAdvisedRoute() throws Exception {
+ // Test that the consumer continues to work properly across multiple
messages
+ MockEndpoint kafkaStub = getMockEndpoint("mock:kafka-advised");
+ MockEndpoint result = getMockEndpoint("mock:advice-result-1");
+
+ kafkaStub.expectedMessageCount(3);
+ result.expectedMessageCount(3);
+
+ // Send multiple messages
+ for (int i = 0; i < 3; i++) {
+ template.sendBody("kafka:" + TOPIC_ADVISED, "Message " + i);
+ }
+
+ kafkaStub.assertIsSatisfied(10000);
+ result.assertIsSatisfied(10000);
+ }
+}
diff --git
a/integration-tests/kafka/src/test/java/org/apache/camel/quarkus/component/kafka/it/KafkaPerTestAdviceExample.java
b/integration-tests/kafka/src/test/java/org/apache/camel/quarkus/component/kafka/it/KafkaPerTestAdviceExample.java
new file mode 100644
index 0000000000..a1a200f7b3
--- /dev/null
+++
b/integration-tests/kafka/src/test/java/org/apache/camel/quarkus/component/kafka/it/KafkaPerTestAdviceExample.java
@@ -0,0 +1,113 @@
+/*
+ * 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.quarkus.component.kafka.it;
+
+import java.util.UUID;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.quarkus.component.kafka.CamelKafkaRoutes;
+import org.apache.camel.quarkus.test.CamelQuarkusTestSupport;
+import org.apache.camel.quarkus.test.support.kafka.KafkaTestResource;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.builder.Builder.constant;
+import static org.apache.camel.builder.Builder.simple;
+
+/**
+ * Example: Applying different AdviceWith per test method using the {@code
adviceRoute()} utility.
+ * <p>
+ * This test advises an EXISTING route (defined in CamelKafkaRoutes.java)
+ * with DIFFERENT advice in each test method.
+ * <p>
+ * This demonstrates using different advice per test without manual route
lifecycle management.
+ */
+@QuarkusTest
+@QuarkusTestResource(KafkaTestResource.class)
+public class KafkaPerTestAdviceExample extends CamelQuarkusTestSupport {
+
+ private static final String TOPIC = "per-test-advice-topic";
+
+ @Test
+ void testAdviceToMock() throws Exception {
+ // Advise an existing route with the first variation
+ adviceRoute("per-test-advice-route", route -> {
+
route.weaveByToUri("kafka:per-test-output-topic*").replace().to("mock:result1");
+ });
+
+ // Now test with the advised route
+ MockEndpoint result = getMockEndpoint("mock:result1");
+ String messageBody = "Test message " + UUID.randomUUID();
+
+ result.expectedMessageCount(1);
+ result.expectedBodiesReceived("Processed: " + messageBody);
+
+ template.sendBody("kafka:" + TOPIC, messageBody);
+
+ result.assertIsSatisfied(10000);
+ }
+
+ @Test
+ void testAdviceToDifferentMock() throws Exception {
+ // Apply DIFFERENT advice to the same existing route
+ adviceRoute("per-test-advice-route", route -> {
+ // Different advice: replace with a different mock and add a
transform
+ route.weaveByToUri("kafka:per-test-output-topic*").replace()
+ .transform(simple("TRANSFORMED: ${body}"))
+ .to("mock:result2");
+ });
+
+ // Test with the differently advised route
+ MockEndpoint result = getMockEndpoint("mock:result2");
+ String messageBody = "Another message " + UUID.randomUUID();
+
+ result.expectedMessageCount(1);
+ result.expectedBodiesReceived("TRANSFORMED: Processed: " +
messageBody);
+
+ template.sendBody("kafka:" + TOPIC, messageBody);
+
+ result.assertIsSatisfied(10000);
+ }
+
+ @Test
+ void testAdviceToAddStep() throws Exception {
+ // Yet another different advice variation on the same existing route
+ adviceRoute("per-test-advice-route", route -> {
+ route.weaveByToUri("kafka:per-test-output-topic*")
+ .before()
+ .setHeader("AdviceApplied", constant(true))
+ .to("mock:intercepted");
+
+
route.weaveByToUri("kafka:per-test-output-topic*").replace().to("mock:result3");
+ });
+
+ MockEndpoint intercepted = getMockEndpoint("mock:intercepted");
+ MockEndpoint result = getMockEndpoint("mock:result3");
+ String messageBody = "Third message " + UUID.randomUUID();
+
+ intercepted.expectedMessageCount(1);
+ intercepted.expectedHeaderReceived("AdviceApplied", true);
+ result.expectedMessageCount(1);
+
+ template.sendBody("kafka:" + TOPIC, messageBody);
+
+ intercepted.assertIsSatisfied(10000);
+ result.assertIsSatisfied(10000);
+ }
+}
diff --git
a/test-framework/camel-quarkus-junit/src/main/java/org/apache/camel/quarkus/test/CallbackUtil.java
b/test-framework/camel-quarkus-junit/src/main/java/org/apache/camel/quarkus/test/CallbackUtil.java
index 2ca129b7ba..8d94f63ced 100644
---
a/test-framework/camel-quarkus-junit/src/main/java/org/apache/camel/quarkus/test/CallbackUtil.java
+++
b/test-framework/camel-quarkus-junit/src/main/java/org/apache/camel/quarkus/test/CallbackUtil.java
@@ -24,10 +24,14 @@ import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.engine.execution.NamespaceAwareStore;
import org.junit.platform.engine.support.store.NamespacedHierarchicalStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import static org.junit.platform.engine.support.store.Namespace.GLOBAL;
public class CallbackUtil {
+ private static final Logger LOG =
LoggerFactory.getLogger(CallbackUtil.class);
+
private CallbackUtil() {
// Utility class
}
@@ -49,7 +53,6 @@ public class CallbackUtil {
//because routes will be created again (in case of
TestInstance.Lifecycle.PER_CLASS, this method is not executed)
Set<String> createdRoutes = testInstance.getCreatedRoutes();
if (testInstance.isUseRouteBuilder() && createdRoutes != null) {
-
try {
for (String r : createdRoutes) {
testInstance.context().getRouteController().stopRoute(r);
@@ -60,7 +63,21 @@ public class CallbackUtil {
}
}
- testInstance.context().getComponentNames().forEach(cn ->
testInstance.context().removeComponent(cn));
+ // Restore original route definitions to clear any advice that was
applied
+ testInstance.restoreOriginalRouteDefinitions();
+
+ // Only remove components created by the test, not pre-existing ones
+ Set<String> existingComponents = testInstance.getExistingComponents();
+ if (existingComponents != null) {
+ testInstance.context().getComponentNames().forEach(cn -> {
+ if (!existingComponents.contains(cn)) {
+ LOG.debug("Removing test-created component: {}", cn);
+ testInstance.context().removeComponent(cn);
+ } else {
+ LOG.debug("Preserving existing component: {}", cn);
+ }
+ });
+ }
MockEndpoint.resetMocks(testInstance.context());
}
diff --git
a/test-framework/camel-quarkus-junit/src/main/java/org/apache/camel/quarkus/test/CamelQuarkusTestSupport.java
b/test-framework/camel-quarkus-junit/src/main/java/org/apache/camel/quarkus/test/CamelQuarkusTestSupport.java
index e39f41cb52..7a269ae6ef 100644
---
a/test-framework/camel-quarkus-junit/src/main/java/org/apache/camel/quarkus/test/CamelQuarkusTestSupport.java
+++
b/test-framework/camel-quarkus-junit/src/main/java/org/apache/camel/quarkus/test/CamelQuarkusTestSupport.java
@@ -17,7 +17,10 @@
package org.apache.camel.quarkus.test;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -31,6 +34,8 @@ import org.apache.camel.NoSuchEndpointException;
import org.apache.camel.Processor;
import org.apache.camel.Route;
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.component.mock.MockEndpoint;
import org.apache.camel.model.ModelCamelContext;
@@ -49,6 +54,8 @@ import org.apache.camel.test.junit6.TestSupport;
import org.apache.camel.test.junit6.util.ExtensionHelper;
import org.apache.camel.test.junit6.util.RouteCoverageDumperExtension;
import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.function.ThrowingConsumer;
+import org.eclipse.microprofile.config.ConfigProvider;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.ExtensionContext;
@@ -109,6 +116,8 @@ public class CamelQuarkusTestSupport extends
AbstractTestSupport
@Inject
protected CamelContext context;
private Set<String> createdRoutes;
+ private Map<String, List<ProcessorDefinition<?>>> originalRouteOutputs;
+ private Set<String> existingComponents;
public CamelQuarkusTestSupport() {
super(new CustomTestExecutionConfiguration(), new
CustomCamelContextConfiguration());
@@ -131,7 +140,10 @@ public class CamelQuarkusTestSupport extends
AbstractTestSupport
.withRouteFilterExcludePattern(getRouteFilterExcludePattern())
.withRouteFilterIncludePattern(getRouteFilterIncludePattern())
.withMockEndpoints(isMockEndpoints())
- .withMockEndpointsAndSkip(isMockEndpointsAndSkip());
+ .withMockEndpointsAndSkip(isMockEndpointsAndSkip())
+ .withShutdownTimeout(ConfigProvider.getConfig()
+ .getOptionalValue("camel.main.shutdownTimeout",
Integer.class)
+ .orElse(10));
//CQ starts and stops context with the application start/stop
testConfiguration().withAutoStartContext(false);
@@ -306,9 +318,6 @@ public class CamelQuarkusTestSupport extends
AbstractTestSupport
// noop
}
- /**
- * 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
@@ -391,6 +400,22 @@ public class CamelQuarkusTestSupport extends
AbstractTestSupport
.map(Route::getRouteId)
.collect(Collectors.toSet());
}
+
+ // Save a snapshot of route outputs before any advice is applied
+ // This allows us to restore routes to their original state between
tests
+ ModelCamelContext modelContext =
context.getCamelContextExtension().getContextPlugin(ModelCamelContext.class);
+ if (modelContext != null) {
+ originalRouteOutputs = new HashMap<>();
+ for (RouteDefinition routeDef :
modelContext.getRouteDefinitions()) {
+ // Save a copy of the outputs list (the list itself, not
cloning each processor)
+ List<ProcessorDefinition<?>> outputs = new
ArrayList<>(routeDef.getOutputs());
+ originalRouteOutputs.put(routeDef.getId(), outputs);
+ }
+ }
+
+ // Save a snapshot of existing components before the test creates any
new ones
+ // This allows us to only remove test-created components during cleanup
+ existingComponents = new HashSet<>(context.getComponentNames());
}
/**
@@ -427,12 +452,38 @@ public class CamelQuarkusTestSupport extends
AbstractTestSupport
}
/**
- * Override when using <a
href="http://camel.apache.org/advicewith.html">advice with</a> and return
<code>true</code>.
+ * Override when using <a
href="https://camel.apache.org/manual/advice-with.html">advice with</a> and
return
+ * <code>true</code>.
+ * <p/>
+ * <b>Advice Cleanup:</b> AdviceWith modifications are automatically
cleaned up between test methods. This ensures
+ * that advice applied in one test does not persist to subsequent tests,
preventing interference between tests.
+ * <p/>
+ * <b>Fundamental Limitation:</b> In Camel Quarkus, the CamelContext is
assembled at build time and shared across
+ * all tests in a module. Unlike standalone Camel or Spring Boot (where
the context can be manually started in each
+ * test), you cannot replay the build steps to get a fresh context. This
means:
+ * <ul>
+ * <li>Routes may have already auto-started before advice can be
applied</li>
+ * <li>Routes that connect to external systems (Kafka, JMS, etc.) may
briefly attempt to connect before being
+ * suspended</li>
+ * <li>The context cannot be fully recreated between tests without using
different {@code @TestProfile}s</li>
+ * </ul>
* <p/>
- * <b>Important:</b> You must execute method {@link
#startRouteDefinitions()}} manually from the unit test
- * after you are done doing all the advice with.
+ * <b>Workaround for Auto-Starting Routes:</b> If routes must not start
(e.g., to avoid connection attempts), use
+ * {@code autoStartup=false} in your route definition or via properties
(e.g.,
+ * {@code quarkus.camel.routes.auto-startup=false}). When using this
deprecated approach, you must manually call
+ * {@link #startRouteDefinitions()} after applying advice.
+ * <p/>
+ * <b>Recommended Approach:</b> Instead of using this flag, apply advice in
+ * {@link #doBeforeEach(QuarkusTestMethodContext)}.
+ * This approach is simpler and works reliably for both routes defined in
the test class and global routes
+ * (YAML routes or RouteBuilder beans). Routes that aren't advised will be
automatically started after
+ * {@code doBeforeEach()} completes. Note that the fundamental build-time
limitation still applies.
*
- * @return <code>true</code> to apply advice to existing route(s).
<code>false</code> to disable advice.
+ * @return <code>true</code> to suspend context during setup for
applying advice. <code>false</code> to disable.
+ * @deprecated While this method works, it has fundamental lifecycle
limitations in Camel Quarkus due to the
+ * build-time context assembly. The recommended approach is to
apply advice in
+ * {@link #doBeforeEach(QuarkusTestMethodContext)}. Advice
cleanup between tests and automatic route
+ * starting happen automatically regardless of which approach
you use.
*/
@Override
@Deprecated(since = "3.15.0")
@@ -442,9 +493,18 @@ public class CamelQuarkusTestSupport extends
AbstractTestSupport
/**
* Helper method to start routeDefinitions (to be used with `adviceWith`).
+ * <p>
+ * <b>Note:</b> This method is only necessary when using the deprecated
{@link #isUseAdviceWith()} approach.
+ * If you apply AdviceWith in {@link
#doBeforeEach(QuarkusTestMethodContext)}, route definitions are
+ * automatically started after {@code doBeforeEach()} completes, so you
don't need to call this method.
+ * <p>
+ * When using {@code isUseAdviceWith() = true}, you must still call this
method manually after applying
+ * advice in your {@code @BeforeEach} methods to start routes that weren't
advised.
+ *
+ * @throws Exception if unable to start route definitions
*/
protected void startRouteDefinitions() throws Exception {
- ModelCamelContext modelCamelContext = (ModelCamelContext) context;
+ ModelCamelContext modelCamelContext =
context.getCamelContextExtension().getContextPlugin(ModelCamelContext.class);
List<RouteDefinition> definitions = new
ArrayList<>(modelCamelContext.getRouteDefinitions());
for (Route r : context.getRoutes()) {
//existing route does not need to be started
@@ -453,6 +513,63 @@ public class CamelQuarkusTestSupport extends
AbstractTestSupport
modelCamelContext.startRouteDefinitions(definitions);
}
+ /**
+ * Helper method to apply AdviceWith to a route.
+ * <p>
+ * This method should be used to advise routes that already exist in your
application
+ * code (src/main/java), and NOT routes created via {@link
#createRouteBuilder()} in your test class.
+ * Advising test-created routes can cause unpredictable behavior.
+ * <p>
+ * This method handles the route lifecycle automatically:
+ * <ol>
+ * <li>Stops the route</li>
+ * <li>Applies the advice</li>
+ * <li>Starts the route</li>
+ * </ol>
+ * <p>
+ * You can use this when you need <b>different</b> advice per test method
on existing routes
+ * <p>
+ * <b>Example - advising existing application route:</b>
+ *
+ * <pre>
+ * @code
+ * @QuarkusTest
+ * public class MyRouteTest extends CamelQuarkusTestSupport {
+ * @Test
+ * public void testWithMock() throws Exception {
+ * // Advise an existing route from your application
+ * adviceRoute("existing-route-id", route -> {
+ *
route.weaveByToUri("kafka:real-topic").replace().to("mock:result");
+ * });
+ * // ... test assertions
+ * }
+ * }
+ * </pre>
+ * <p>
+ *
+ * @param routeId the ID of the <b>EXISTING</b> route to advise (must
be from application code, not test)
+ * @param advice the AdviceWith configuration
+ * @throws Exception if unable to apply advice or manage route lifecycle
+ * @see #isUseRouteBuilder()
+ * @see #createRouteBuilder()
+ */
+ protected void adviceRoute(String routeId,
ThrowingConsumer<AdviceWithRouteBuilder, Exception> advice) throws Exception {
+ // Check for Camel Quarkus advice antipattern
+ if (createdRoutes != null && createdRoutes.contains(routeId)) {
+ LOG.warn(
+ "AdviceWith detected on route '{}' which was created in
from a test createRouteBuilder() override. This may cause unpredictable
behavior."
+ +
+ "\nRefer to the Camel Quarkus testing guide
AdviceWith examples section for more details."
+ +
+
"\nhttps://camel.apache.org/camel-quarkus/latest/reference/testing.html",
+ routeId);
+ }
+
+ context.getRouteController().stopRoute(routeId);
+ AdviceWith.adviceWith(context, routeId, advice);
+ context.getRouteController().startRoute(routeId);
+ }
+
/**
* Resolves the mandatory Mock endpoint using a URI of the form
<code>mock:someName</code>
*
@@ -504,6 +621,77 @@ public class CamelQuarkusTestSupport extends
AbstractTestSupport
return createdRoutes;
}
+ Set<String> getExistingComponents() {
+ return existingComponents;
+ }
+
+ /**
+ * Restores routes to their original state by resetting outputs that were
modified by AdviceWith.
+ * This is called after each test to ensure advice doesn't persist across
tests.
+ */
+ void restoreOriginalRouteDefinitions() {
+ if (originalRouteOutputs == null || originalRouteOutputs.isEmpty()) {
+ return;
+ }
+
+ ModelCamelContext modelContext =
context.getCamelContextExtension().getContextPlugin(ModelCamelContext.class);
+ if (modelContext == null) {
+ return;
+ }
+
+ // Restore routes that have been modified by advice
+ for (RouteDefinition routeDef : modelContext.getRouteDefinitions()) {
+ String routeId = routeDef.getId();
+ List<ProcessorDefinition<?>> originalOutputs =
originalRouteOutputs.get(routeId);
+
+ if (originalOutputs != null) {
+ List<ProcessorDefinition<?>> currentOutputs =
routeDef.getOutputs();
+ boolean wasModified = false;
+
+ // Check if the route was modified by comparing size or
processor instances
+ if (currentOutputs.size() != originalOutputs.size()) {
+ wasModified = true;
+ } else {
+ // Same size, but check if processors were replaced
+ for (int i = 0; i < currentOutputs.size(); i++) {
+ if (currentOutputs.get(i) != originalOutputs.get(i)) {
+ wasModified = true;
+ break;
+ }
+ }
+ }
+
+ if (wasModified) {
+ // Check if user is advising a test-created route which is
considered an antipattern on Camel Quarkus
+ if (createdRoutes != null &&
createdRoutes.contains(routeId)) {
+ LOG.warn(
+ "AdviceWith detected on route '{}' which was
created in from a test createRouteBuilder() override. This may cause
unpredictable behavior."
+ +
+ "\nRefer to the Camel Quarkus testing
guide AdviceWith examples section for more details."
+ +
+
"\nhttps://camel.apache.org/camel-quarkus/latest/reference/testing.html",
+ routeId);
+ }
+
+ try {
+ // Stop the route before modifying it
+ context.getRouteController().stopRoute(routeId);
+
+ // Clear current outputs and restore original ones
+ currentOutputs.clear();
+ currentOutputs.addAll(originalOutputs);
+
+ // Restart the route
+ context.getRouteController().startRoute(routeId);
+ } catch (Exception e) {
+ LOG.warn("Failed to restore route '{}' after advice",
routeId, e);
+ }
+ }
+
+ }
+ }
+ }
+
private void assertTestClassCamelContextMatchesAppCamelContext() {
// Test classes must use the same CamelContext as the application
under test
Assertions.assertEquals(context, contextManager.context(),
diff --git
a/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/AdviceRouteInDoBeforeEachTest.java
b/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/AdviceRouteInDoBeforeEachTest.java
new file mode 100644
index 0000000000..18f241a823
--- /dev/null
+++
b/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/AdviceRouteInDoBeforeEachTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.quarkus.test.junit6;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.TestProfile;
+import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.quarkus.test.CamelQuarkusTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.builder.Builder.simple;
+
+/**
+ * Test that verifies using adviceRoute() in doBeforeEach() works correctly.
+ * <p>
+ * While using adviceRoute() in doBeforeEach() works, it's not necessary since
routes
+ * aren't started yet. Using AdviceWith.adviceWith() directly is cleaner in
this context.
+ * <p>
+ * This test exists to verify that if users mistakenly use adviceRoute() in
doBeforeEach(),
+ * it still works correctly:
+ * <ol>
+ * <li>adviceRoute() stops, advises, and starts the route</li>
+ * <li>Auto-start runs afterward but skips routes that are already started</li>
+ * <li>Advice cleanup still works based on route definition comparison</li>
+ * </ol>
+ */
+@QuarkusTest
+@TestProfile(AdviceRouteInDoBeforeEachTest.class)
+public class AdviceRouteInDoBeforeEachTest extends CamelQuarkusTestSupport {
+
+ @Override
+ protected void doBeforeEach(QuarkusTestMethodContext context) throws
Exception {
+ // Using adviceRoute() in doBeforeEach - works but not necessary
+ // (direct AdviceWith is cleaner here since routes aren't started yet)
+ adviceRoute("test-route", route -> {
+ route.weaveByToUri("seda:*").replace().to("mock:seda-stub");
+ });
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:input1")
+ .routeId("test-route")
+ .setBody(simple("Processed: ${body}"))
+ .to("seda:output")
+ .to("mock:result1");
+
+ from("direct:input2")
+ .routeId("other-route")
+ .setBody(simple("Direct: ${body}"))
+ .to("mock:result2");
+ }
+ };
+ }
+
+ @Test
+ void testAdvisedRoute() throws Exception {
+ MockEndpoint sedaStub = getMockEndpoint("mock:seda-stub");
+ MockEndpoint result = getMockEndpoint("mock:result1");
+
+ sedaStub.expectedMessageCount(1);
+ sedaStub.expectedBodiesReceived("Processed: test1");
+ result.expectedMessageCount(1);
+
+ template.sendBody("direct:input1", "test1");
+
+ sedaStub.assertIsSatisfied();
+ result.assertIsSatisfied();
+ }
+
+ @Test
+ void testNonAdvisedRouteStillWorks() throws Exception {
+ // Verifies auto-start worked for other-route even though we used
adviceRoute()
+ MockEndpoint result = getMockEndpoint("mock:result2");
+
+ result.expectedMessageCount(1);
+ result.expectedBodiesReceived("Direct: test2");
+
+ template.sendBody("direct:input2", "test2");
+
+ result.assertIsSatisfied();
+ }
+}
diff --git
a/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/AdviceRouteUtilityTest.java
b/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/AdviceRouteUtilityTest.java
new file mode 100644
index 0000000000..033f42497b
--- /dev/null
+++
b/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/AdviceRouteUtilityTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.quarkus.test.junit6;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.TestProfile;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.quarkus.test.CamelQuarkusTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.builder.Builder.constant;
+import static org.apache.camel.builder.Builder.simple;
+
+/**
+ * Test demonstrating the adviceRoute() utility method for applying different
+ * advice per test method. This utility handles route lifecycle (stop, advice,
start)
+ * automatically, making per-test-method advice much cleaner.
+ */
+@QuarkusTest
+@TestProfile(AdviceRouteUtilityTest.class)
+public class AdviceRouteUtilityTest extends CamelQuarkusTestSupport {
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:input")
+ .routeId("test-route")
+ .setBody(simple("Processed: ${body}"))
+ .to("seda:output")
+ .to("mock:result");
+ }
+ };
+ }
+
+ @Test
+ void testAdviceToReplaceSeda() throws Exception {
+ // Use adviceRoute() utility - it handles stop/advice/start
automatically
+ adviceRoute("test-route", route -> {
+ route.weaveByToUri("seda:output").replace().to("mock:seda-stub");
+ });
+
+ MockEndpoint stub = getMockEndpoint("mock:seda-stub");
+ MockEndpoint result = getMockEndpoint("mock:result");
+
+ stub.expectedMessageCount(1);
+ stub.expectedBodiesReceived("Processed: test1");
+ result.expectedMessageCount(1);
+
+ template.sendBody("direct:input", "test1");
+
+ stub.assertIsSatisfied();
+ result.assertIsSatisfied();
+ }
+
+ @Test
+ void testAdviceToAddTransformation() throws Exception {
+ // Different advice for this test - add a transformation step
+ adviceRoute("test-route", route -> {
+ route.weaveByToUri("seda:output").replace()
+ .transform(simple("TRANSFORMED: ${body}"))
+ .to("mock:seda-stub");
+ });
+
+ MockEndpoint stub = getMockEndpoint("mock:seda-stub");
+ MockEndpoint result = getMockEndpoint("mock:result");
+
+ stub.expectedMessageCount(1);
+ stub.expectedBodiesReceived("TRANSFORMED: Processed: test2");
+ result.expectedMessageCount(1);
+
+ template.sendBody("direct:input", "test2");
+
+ stub.assertIsSatisfied();
+ result.assertIsSatisfied();
+ }
+
+ @Test
+ void testAdviceToAddInterceptor() throws Exception {
+ // Yet another different advice - add an interceptor before the
endpoint
+ adviceRoute("test-route", route -> {
+ route.weaveByToUri("seda:output")
+ .before()
+ .setHeader("Intercepted", constant(true))
+ .to("mock:intercepted");
+
+ route.weaveByToUri("seda:output").replace().to("mock:seda-stub");
+ });
+
+ MockEndpoint intercepted = getMockEndpoint("mock:intercepted");
+ MockEndpoint stub = getMockEndpoint("mock:seda-stub");
+ MockEndpoint result = getMockEndpoint("mock:result");
+
+ intercepted.expectedMessageCount(1);
+ intercepted.expectedHeaderReceived("Intercepted", true);
+ stub.expectedMessageCount(1);
+ result.expectedMessageCount(1);
+
+ template.sendBody("direct:input", "test3");
+
+ intercepted.assertIsSatisfied();
+ stub.assertIsSatisfied();
+ result.assertIsSatisfied();
+ }
+
+ @Test
+ void testAdviceCleanupBetweenTests() throws Exception {
+ // This test verifies that advice from previous tests was cleaned up
+ // If advice wasn't cleaned up, mock:seda-stub would receive messages
+ adviceRoute("test-route", route -> {
+ // Just replace with a different mock to verify cleanup worked
+ route.weaveByToUri("seda:output").replace().to("mock:fresh-stub");
+ });
+
+ MockEndpoint freshStub = getMockEndpoint("mock:fresh-stub");
+ MockEndpoint result = getMockEndpoint("mock:result");
+
+ freshStub.expectedMessageCount(1);
+ result.expectedMessageCount(1);
+
+ template.sendBody("direct:input", "test4");
+
+ freshStub.assertIsSatisfied();
+ result.assertIsSatisfied();
+ }
+}
diff --git
a/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/AdviceWithBeforeEachTest.java
b/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/AdviceWithBeforeEachTest.java
new file mode 100644
index 0000000000..881d149bd6
--- /dev/null
+++
b/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/AdviceWithBeforeEachTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.quarkus.test.junit6;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.TestProfile;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.quarkus.test.CamelQuarkusTestSupport;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.builder.Builder.simple;
+
+/**
+ * Demonstrates the recommended AdviceWith pattern using @BeforeEach.
+ * <p>
+ * This pattern is recommended when all tests in the class use the same advice.
+ * Key features:
+ * <ul>
+ * <li>Routes that aren't advised are automatically started after @BeforeEach
completes</li>
+ * <li>Advice modifications are automatically cleaned up between test
methods</li>
+ * <li>No need to manually call startRouteDefinitions()</li>
+ * </ul>
+ */
+@QuarkusTest
+@TestProfile(AdviceWithBeforeEachTest.class)
+public class AdviceWithBeforeEachTest extends CamelQuarkusTestSupport {
+
+ @BeforeEach
+ void beforeEach() throws Exception {
+ // Apply advice to route1 only - route2 should be auto-started
+ AdviceWith.adviceWith(this.context, "advised-route", route -> {
+ // Replace the file endpoint with a mock so we can verify the
message was processed
+ route.weaveByToUri("file:*").replace().to("mock:file-stub");
+ });
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ // Route 1: This route will be advised
+ from("direct:input1")
+ .routeId("advised-route")
+ .setBody(simple("Processed: ${body}"))
+ .to("file:target/output")
+ .to("mock:result1");
+
+ // Route 2: This route is NOT advised, but should still work
due to auto-start
+ from("direct:input2")
+ .routeId("non-advised-route")
+ .setBody(simple("Direct: ${body}"))
+ .to("mock:result2");
+ }
+ };
+ }
+
+ @Test
+ void testAdvisedRoute() throws Exception {
+ MockEndpoint fileStub = getMockEndpoint("mock:file-stub");
+ MockEndpoint result = getMockEndpoint("mock:result1");
+
+ fileStub.expectedMessageCount(1);
+ fileStub.expectedBodiesReceived("Processed: message1");
+ result.expectedMessageCount(1);
+
+ template.sendBody("direct:input1", "message1");
+
+ fileStub.assertIsSatisfied();
+ result.assertIsSatisfied();
+ }
+
+ @Test
+ void testNonAdvisedRouteStillWorks() throws Exception {
+ // This verifies that non-advised routes are auto-started
+ MockEndpoint result = getMockEndpoint("mock:result2");
+
+ result.expectedMessageCount(1);
+ result.expectedBodiesReceived("Direct: message2");
+
+ template.sendBody("direct:input2", "message2");
+
+ result.assertIsSatisfied();
+ }
+
+ @Test
+ void testMultipleMessagesOnAdvisedRoute() throws Exception {
+ // Test that the route continues to work properly across multiple
messages
+ MockEndpoint fileStub = getMockEndpoint("mock:file-stub");
+ MockEndpoint result = getMockEndpoint("mock:result1");
+
+ fileStub.expectedMessageCount(3);
+ result.expectedMessageCount(3);
+
+ // Send multiple messages
+ for (int i = 0; i < 3; i++) {
+ template.sendBody("direct:input1", "Message " + i);
+ }
+
+ fileStub.assertIsSatisfied();
+ result.assertIsSatisfied();
+ }
+
+ @Test
+ void testAdviceIsConsistentAcrossTests() throws Exception {
+ // This test verifies that advice is consistently applied even after
other tests run
+ // If advice cleanup failed, this test might see unexpected behavior
+ MockEndpoint fileStub = getMockEndpoint("mock:file-stub");
+ MockEndpoint result = getMockEndpoint("mock:result1");
+
+ fileStub.expectedMessageCount(1);
+ fileStub.expectedBodiesReceived("Processed: consistent");
+ result.expectedMessageCount(1);
+
+ template.sendBody("direct:input1", "consistent");
+
+ fileStub.assertIsSatisfied();
+ result.assertIsSatisfied();
+ }
+}
diff --git
a/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/AutoStartRoutesAfterAdviceTest.java
b/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/AutoStartRoutesAfterAdviceTest.java
new file mode 100644
index 0000000000..e66aec025c
--- /dev/null
+++
b/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/AutoStartRoutesAfterAdviceTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.quarkus.test.junit6;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.TestProfile;
+import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.quarkus.test.CamelQuarkusTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test to demonstrate that startRouteDefinitions() is no longer needed when
using
+ * AdviceWith in doBeforeEach(). Routes that aren't advised are automatically
started.
+ */
+@QuarkusTest
+@TestProfile(AutoStartRoutesAfterAdviceTest.class)
+public class AutoStartRoutesAfterAdviceTest extends CamelQuarkusTestSupport {
+
+ @Override
+ protected void doBeforeEach(QuarkusTestMethodContext context) throws
Exception {
+ // Apply advice to route1 only - route2 should be auto-started
+ AdviceWith.adviceWith(this.context, "route1", route -> {
+ route.weaveByToUri("file:*").replace().to("mock:file");
+ });
+
+ // NOTE: No need to call startRouteDefinitions() anymore!
+ // Routes that weren't advised (like route2) are automatically started
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:test1")
+ .routeId("route1")
+ .setBody(constant("Data 1"))
+ .to("file:target/output1")
+ .to("mock:result1");
+
+ // This route is NOT advised, but should still work
+ from("direct:test2")
+ .routeId("route2")
+ .setBody(constant("Data 2"))
+ .to("mock:result2");
+ }
+ };
+ }
+
+ @Test
+ public void testAdvisedRoute() throws Exception {
+ MockEndpoint fileStub = getMockEndpoint("mock:file");
+ MockEndpoint result = getMockEndpoint("mock:result1");
+
+ fileStub.expectedMessageCount(1);
+ fileStub.expectedBodiesReceived("Data 1");
+ result.expectedMessageCount(1);
+
+ template.sendBody("direct:test1", "ignored");
+
+ fileStub.assertIsSatisfied();
+ result.assertIsSatisfied();
+ }
+
+ @Test
+ public void testNonAdvisedRouteStillWorks() throws Exception {
+ // This verifies that route2 was auto-started even though we only
advised route1
+ MockEndpoint result = getMockEndpoint("mock:result2");
+
+ result.expectedMessageCount(1);
+ result.expectedBodiesReceived("Data 2");
+
+ template.sendBody("direct:test2", "ignored");
+
+ result.assertIsSatisfied();
+ }
+}
diff --git
a/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/GlobalRouteAdviceTest.java
b/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/GlobalRouteAdviceTest.java
new file mode 100644
index 0000000000..4332ee446c
--- /dev/null
+++
b/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/GlobalRouteAdviceTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.quarkus.test.junit6;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.TestProfile;
+import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.quarkus.test.CamelQuarkusTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test to reproduce issue #7264 - AdviceWith with global routes fails on
second test.
+ * This test uses doBeforeEach to apply advice, which should work across
multiple tests.
+ */
+@QuarkusTest
+@TestProfile(GlobalRouteAdviceTest.class)
+public class GlobalRouteAdviceTest extends CamelQuarkusTestSupport {
+
+ /**
+ * Global routes can be simulated by NOT overriding isUseRouteBuilder
(defaults to true)
+ * but having the route already exist in the context before the test runs.
+ * In this test, the route is created via the RouteBuilder and persists
across test methods,
+ * similar to how YAML routes or @Produces RouteBuilder beans behave.
+ */
+
+ @Override
+ protected void doBeforeEach(QuarkusTestMethodContext context) throws
Exception {
+ // Apply advice to the route
+ AdviceWith.adviceWith(this.context, "globalAdviceRoute", route -> {
+ route.weaveAddLast().to("mock:advised");
+ });
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:globalStart")
+ .routeId("globalAdviceRoute")
+ .log("Processing: ${body}")
+ .process(exchange -> {
+ String body =
exchange.getIn().getBody(String.class);
+ exchange.getIn().setBody(body.toUpperCase());
+ })
+ .to("mock:globalResult");
+ }
+ };
+ }
+
+ @Test
+ public void testFirstAdvice() throws Exception {
+ MockEndpoint advised = getMockEndpoint("mock:advised");
+ advised.expectedMessageCount(1);
+ advised.expectedBodiesReceived("HELLO");
+
+ template.sendBody("direct:globalStart", "hello");
+
+ advised.assertIsSatisfied();
+ }
+
+ @Test
+ public void testSecondAdvice() throws Exception {
+ // This test works the same as the first one because automatic cleanup
+ // removes the advice from the first test before this one runs
+ MockEndpoint advised = getMockEndpoint("mock:advised");
+ advised.expectedMessageCount(1);
+ advised.expectedBodiesReceived("WORLD");
+
+ template.sendBody("direct:globalStart", "world");
+
+ advised.assertIsSatisfied();
+ }
+}
diff --git
a/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/GlobalRouteWeaveByIdTest.java
b/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/GlobalRouteWeaveByIdTest.java
new file mode 100644
index 0000000000..4f99671b60
--- /dev/null
+++
b/test-framework/camel-quarkus-junit/src/test/java/org/apache/camel/quarkus/test/junit6/GlobalRouteWeaveByIdTest.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.quarkus.test.junit6;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.TestProfile;
+import io.quarkus.test.junit.callback.QuarkusTestMethodContext;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.quarkus.test.CamelQuarkusTestSupport;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test to reproduce issue #7264 - weaveById fails on second test when using
global routes.
+ * The processor ID cannot be found on subsequent tests because the route
definition has been modified.
+ */
+@QuarkusTest
+@TestProfile(GlobalRouteWeaveByIdTest.class)
+public class GlobalRouteWeaveByIdTest extends CamelQuarkusTestSupport {
+
+ @Override
+ protected void doBeforeEach(QuarkusTestMethodContext context) throws
Exception {
+ // Use weaveById to replace a processor
+ AdviceWith.adviceWith(this.context, "routeWithIds", route -> {
+ route.weaveById("mockEndpoint").replace().to("mock:replaced");
+ });
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:withIds")
+ .routeId("routeWithIds")
+ .log("Step 1").id("step1")
+ .transform().simple("Hello ${body}").id("transform1")
+ .to("mock:withIdsResult").id("mockEndpoint");
+ }
+ };
+ }
+
+ @Test
+ public void testFirstWeaveById() throws Exception {
+ MockEndpoint replaced = getMockEndpoint("mock:replaced");
+ replaced.expectedMessageCount(1);
+ replaced.expectedBodiesReceived("Hello World");
+
+ template.sendBody("direct:withIds", "World");
+
+ replaced.assertIsSatisfied();
+ }
+
+ @Test
+ public void testSecondWeaveById() throws Exception {
+ // This test will likely fail because the processor with id
"mockEndpoint"
+ // no longer exists after the first test replaced it
+ MockEndpoint replaced = getMockEndpoint("mock:replaced");
+ replaced.expectedMessageCount(1);
+ replaced.expectedBodiesReceived("Hello Universe");
+
+ template.sendBody("direct:withIds", "Universe");
+
+ replaced.assertIsSatisfied();
+ }
+}