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 b95bc88278d Split the testing user guide into separate pages
b95bc88278d is described below
commit b95bc88278dd11c4b5607211e7fc8eb5038379f8
Author: James Netherton <[email protected]>
AuthorDate: Tue Oct 20 07:08:10 2026 +0100
Split the testing user guide into separate pages
---
docs/modules/ROOT/nav.adoc | 3 +
.../modules/ROOT/pages/migration-guide/3.36.0.adoc | 2 +-
docs/modules/ROOT/pages/user-guide/testing.adoc | 734 +--------------------
.../testing/camel-quarkus-test-support.adoc | 364 ++++++++++
.../user-guide/testing/camel-quarkus-test.adoc | 209 ++++++
.../user-guide/testing/external-services.adoc | 152 +++++
6 files changed, 734 insertions(+), 730 deletions(-)
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index d26f3b3ef15..648e78b8233 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -9,6 +9,9 @@
** xref:user-guide/native-mode.adoc[Native mode]
** xref:user-guide/command-mode.adoc[Command mode]
** xref:user-guide/testing.adoc[Testing]
+*** xref:user-guide/testing/external-services.adoc[External services]
+*** xref:user-guide/testing/camel-quarkus-test.adoc[Annotation based testing]
+*** xref:user-guide/testing/camel-quarkus-test-support.adoc[CamelTestSupport
style]
** xref:user-guide/security-model.adoc[Security model]
** xref:user-guide/examples.adoc[Examples]
** xref:user-guide/kubernetes.adoc[Kubernetes]
diff --git a/docs/modules/ROOT/pages/migration-guide/3.36.0.adoc
b/docs/modules/ROOT/pages/migration-guide/3.36.0.adoc
index 42b144cd30a..7dd3c68d896 100644
--- a/docs/modules/ROOT/pages/migration-guide/3.36.0.adoc
+++ b/docs/modules/ROOT/pages/migration-guide/3.36.0.adoc
@@ -60,4 +60,4 @@ This is particularly useful when different tests in the same
class need differen
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].
+For more details, see the
xref:user-guide/testing/camel-quarkus-test-support.adoc#_using_advicewith[Testing
guide].
diff --git a/docs/modules/ROOT/pages/user-guide/testing.adoc
b/docs/modules/ROOT/pages/user-guide/testing.adoc
index cac4a378638..25386d005ce 100644
--- a/docs/modules/ROOT/pages/user-guide/testing.adoc
+++ b/docs/modules/ROOT/pages/user-guide/testing.adoc
@@ -7,7 +7,11 @@ Before going deeper into the subject, it is strongly advised
to read xref:user-g
When it comes to testing a route in the context of Quarkus, the recommended
approach is to write local integration tests.
This has the advantage of covering both JVM and native mode.
-xref:#_cameltestsupport_style_of_testing[`CamelTestSupport`] style testing can
also be used. Note that it can only work in JVM mode.
+The following pages cover further testing topics:
+
+* xref:user-guide/testing/external-services.adoc[Testing with external
services]
+* xref:user-guide/testing/camel-quarkus-test.adoc[Annotation based testing
with `@CamelQuarkusTest`] (JVM mode only)
+* xref:user-guide/testing/camel-quarkus-test-support.adoc[`CamelTestSupport`
style of testing] (JVM mode only)
== A test running in JVM mode
@@ -74,731 +78,3 @@ must take one or more of the following forms:
* If a container image was created during the build, then a container is
started so that tests can be executed against it.
For more information about `QuarkusIntegrationTest` see the
https://quarkus.io/guides/getting-started-testing#quarkus-integration-test[Quarkus
testing guide].
-
-== Testing with external services
-
-=== Testcontainers
-
-Sometimes your application needs to access an external service such as a
message broker, database, etc.
-If a container image is available for the service of interest,
https://www.testcontainers.org/[Testcontainers]
-can be used to start and stop these services for testing.
-
-For the application to work properly, it is often essential to pass the
connection configuration data
-(host, port, user, password, etc. of the remote service) to the application
before it starts.
-In the Quarkus ecosystem, `QuarkusTestResourceLifecycleManager` serves this
purpose.
-
-You can start one or more containers in the `start()` method
-and return the connection configuration in the form of a `Map`, where the key
is the name of a configuration property and the value is the property value.
-The entries of this map are then passed to the application either via command
line (`-Dkey=value`) in native mode,
-or through a special MicroProfile configuration provider in JVM mode.
-Note that these settings have a higher precedence than the settings in
`application.properties` file.
-
-[source,java]
-----
-import java.util.Map;
-import java.util.HashMap;
-
-import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
-import org.testcontainers.containers.GenericContainer;
-import org.testcontainers.containers.wait.strategy.Wait;
-
-public class MyTestResource implements QuarkusTestResourceLifecycleManager {
-
- private GenericContainer<?> myContainer;
-
- @Override
- public Map<String, String> start() {
- // Start the needed container(s)
- myContainer = new
GenericContainer(DockerImageName.parse("my/image:1.0.0"))
- .withExposedPorts(1234)
- .waitingFor(Wait.forListeningPort());
-
- myContainer.start();
-
- // Pass the configuration to the application under test
- // You can also pass camel component property names / values to
automatically configure Camel components
- return new HashMap<>() {{
- put("my-container.host", container.getHost());
- put("my-container.port", "" + container.getMappedPort(1234));
- }};
- }
-
- @Override
- public void stop() {
- // Stop the needed container(s)
- myContainer.stop();
- }
-}
-----
-
-The defined test resource needs to be referenced from the test classes with
`@QuarkusTestResource` as shown below:
-
-[source,java]
-----
-import io.quarkus.test.common.QuarkusTestResource;
-import io.quarkus.test.junit.QuarkusTest;
-
-@QuarkusTest
-@QuarkusTestResource(MyTestResource.class)
-class MyTest {
- ...
-}
-----
-
-More information can be found in the
https://quarkus.io/guides/getting-started-testing#quarkus-test-resource[Quarkus
testing guide]. Refer to Camel Quarkus source tree for a
https://github.com/apache/camel-quarkus/blob/main/integration-tests/google-storage/src/test/java/org/apache/camel/quarkus/component/google/storage/it/GoogleStorageTestResource.java[complete
example].
-
-=== WireMock
-
-It is sometimes useful to stub HTTP interactions with third party services &
APIs so that tests do not have to connect to live endpoints, as this can incur
costs and the service may not always be 100% available or reliable.
-
-An excellent tool for mocking & recording HTTP interactions is
http://wiremock.org/[WireMock]. It is used extensively throughout the Camel
Quarkus test suite for various component extensions. Here follows a typical
workflow
-for setting up WireMock.
-
-First set up the WireMock server. Note that it is important to configure the
Camel component under test to pass any HTTP interactions through the WireMock
proxy. This is usually achieved by configuring a component property
-that determines the API endpoint URL. Sometimes things are less
straightforward and some extra work is required to configure the API client
library, as was the case for
https://github.com/apache/camel-quarkus/blob/main/integration-tests/twilio/src/main/java/org/apache/camel/quarkus/component/twilio/it/TwilioResource.java#L83[Twilio].
-
-[source,java]
-----
-import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
-import static com.github.tomakehurst.wiremock.client.WireMock.get;
-import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
-import static
com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import com.github.tomakehurst.wiremock.WireMockServer;
-
-import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
-
-public class WireMockTestResource implements
QuarkusTestResourceLifecycleManager {
-
- private WireMockServer server;
-
- @Override
- public Map<String, String> start() {
- // Setup & start the server
- server = new WireMockServer(
- wireMockConfig().dynamicPort()
- );
- server.start();
-
- // Stub an HTTP endpoint. Note that WireMock also supports a record
and playback mode
- // https://wiremock.org/docs/record-playback/
- server.stubFor(
- get(urlEqualTo("/api/greeting"))
- .willReturn(aResponse()
- .withHeader("Content-Type", "application/json")
- .withBody("{\"message\": \"Hello World\"}")));
-
- // Ensure the camel component API client passes requests through the
WireMock proxy
- Map<String, String> conf = new HashMap<>();
- conf.put("camel.component.foo.server-url", server.baseUrl());
- return conf;
- }
-
- @Override
- public void stop() {
- if (server != null) {
- server.stop();
- }
- }
-}
-----
-
-Finally, ensure your test class has the `@QuarkusTestResource` annotation with
the appropriate test resource class specified as the value. The WireMock server
will be started before all tests are
-executed and will be shut down when all tests are finished.
-
-[source,java]
-----
-import io.quarkus.test.common.QuarkusTestResource;
-import io.quarkus.test.junit.QuarkusTest;
-
-@QuarkusTest
-@QuarkusTestResource(WireMockTestResource.class)
-class MyTest {
- ...
-}
-----
-
-More examples of WireMock usage can be found in the Camel Quarkus integration
test source tree such as in the
https://github.com/apache/camel-quarkus/blob/main/integration-tests/validator/src/test/java/org/apache/camel/quarkus/component/validator/it/ValidatorTestResource.java[validator
tests].
-
-=== Citrus Testing Framework
-
-The https://citrusframework.org/[Citrus Testing Framework] provides advanced
integration testing capabilities for Camel Quarkus applications.
-For detailed information about using Citrus with Quarkus test support, see
xref:manual::integration-test.adoc#_quarkus_test_support[Integration Testing
with Citrus] in the Apache Camel documentation.
-
-== Annotation based testing with `@CamelQuarkusTest`
-
-`@CamelQuarkusTest` configures Camel specific test features with annotation
attributes, without extending a base class.
-It implies `@QuarkusTest`.
-
-IMPORTANT: `@CamelQuarkusTest` only works in JVM mode.
-
-To use `@CamelQuarkusTest`, add `camel-quarkus-junit` as a test scoped
dependency to your application.
-
-[source,xml]
-----
-<dependency>
- <groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-junit</artifactId>
- <scope>test</scope>
-</dependency>
-----
-
-Everything declared on `@CamelQuarkusTest` applies to the whole test class.
-It is set up before the first test method runs and undone after the last one.
-`MockEndpoint` instances are reset after each test method.
-
-=== Test routes
-
-Routes for a test class are declared with `routes`.
-They are added before the first test method and removed after the last one.
-Route builder classes declared in a `@CamelQuarkusTest` class, or listed in
`routes`, are never discovered as application routes.
-
-`CamelContext`, `ProducerTemplate`, `FluentProducerTemplate` and
`ConsumerTemplate` can be injected with `@Inject`, and endpoints with
`@EndpointInject`.
-
-[source,java]
-----
-@CamelQuarkusTest(routes = GreetingTest.Routes.class)
-class GreetingTest {
-
- @Inject
- ProducerTemplate template;
-
- @EndpointInject("mock:result")
- MockEndpoint result;
-
- @Test
- void greets() throws Exception {
- result.expectedBodiesReceived("Hello World");
- template.sendBody("direct:start", "World");
- result.assertIsSatisfied();
- }
-
- public static class Routes extends RouteBuilder {
- @Override
- public void configure() {
- from("direct:start").transform().simple("Hello
${body}").to("mock:result");
- }
- }
-}
-----
-
-=== Advising application routes
-
-Advice for the test class is declared with `advices`, and route consumers are
replaced with `replaceRouteFromWith`.
-
-[source,java]
-----
-@CamelQuarkusTest(
- advices = @AdviceRoute(route = "my-kafka-route", advice =
KafkaRouteTest.StubKafka.class),
- replaceRouteFromWith = "my-timer-route=direct:trigger")
-class KafkaRouteTest {
-
- static class StubKafka extends AdviceWithRouteBuilder {
- @Override
- public void configure() {
-
weaveByToUri("kafka:output-topic*").replace().to("mock:kafka-stub");
- }
- }
-}
-----
-
-Advice for a single test method is applied with `CamelTestHelper`, which can
be injected into the test class.
-It is undone after the test method.
-
-[source,java]
-----
-@CamelQuarkusTest
-class MyRouteTest {
-
- @Inject
- CamelTestHelper helper;
-
- @Test
- void withMock() throws Exception {
- helper.adviceRoute("my-route", route ->
route.weaveByToUri("kafka:*").replace().to("mock:kafka"));
- // ...
- }
-}
-----
-
-`CamelTestHelper` also provides `getMockEndpoint`, `resetMocks` and
`startRouteDefinitions`.
-
-=== Replacing beans in the Camel registry
-
-Fields and no-arg methods annotated with `@ReplaceInRegistry` are bound to the
Camel registry, using the member name as the bean name.
-The previous bindings are restored after the last test method.
-The beans are bound before the routes declared with `routes` are added.
Application routes already exist at that point, as do the routes declared with
`routes` in <<_shared_mode_and_isolated_mode,isolated mode>>, so routes that
look up a bean when they are created do not use the replacement.
-Beans provided by CDI cannot be replaced this way. Use
https://quarkus.io/guides/getting-started-testing#mock-support[`@InjectMock` or
`QuarkusMock`] for those.
-
-[source,java]
-----
-@CamelQuarkusTest
-class GreeterTest {
-
- @ReplaceInRegistry
- Greeter greeter = new FakeGreeter();
-}
-----
-
-=== Shared mode and isolated mode
-
-By default, a test class shares the application with the other test classes of
the module, and its routes are already started when the test class begins.
-Some features need to act before routes are created. They are only available
when the test class owns its application, which is called isolated mode.
-
-A test class runs in isolated mode when it is annotated with `@TestProfile`
and no other `@CamelQuarkusTest` class uses the same test profile.
-The usual way is for the test class to be its own test profile, in which case
it must be public.
-
-[source,java]
-----
-@CamelQuarkusTest(mockEndpointsAndSkip = "kafka:*")
-@TestProfile(OrdersTest.class)
-public class OrdersTest implements QuarkusTestProfile {
-}
-----
-
-Using a feature that is only available in isolated mode fails the test build.
-
-[cols="2,1,1"]
-|===
-| Feature | Shared mode | Isolated mode
-
-| `routes`, `advices`, `replaceRouteFromWith`, `@ReplaceInRegistry`
-| Yes
-| Yes
-
-| `mockEndpoints`, `mockEndpointsAndSkip`, `stubEndpoints`
-| No
-| Yes
-
-| `autoStartupExclude`
-| No
-| Yes
-
-| `@ProvidesBreakpoint`
-| No
-| Yes
-
-| `dumpRouteCoverage`, `dumpRoute`, `shutdownTimeout`
-| Yes
-| Yes
-|===
-
-In isolated mode, `advices` and `replaceRouteFromWith` are applied before
routes are created.
-
-=== Debugging, route coverage and route dumps
-
-A `public static` no-arg method annotated with `@ProvidesBreakpoint` and
returning `org.apache.camel.spi.Breakpoint` enables the Camel debugger with
that breakpoint.
-
-`dumpRouteCoverage = true` writes route coverage to
`target/camel-route-coverage` after each test method. It requires
`camel-quarkus-management`.
-
-`dumpRoute` writes the routes to `target/camel-route-dump` after each test
method, in `xml` or `yaml` format.
-It requires `camel-quarkus-xml-io-dsl` for `xml`, and `camel-quarkus-yaml-io`
for `yaml`.
-
-=== Continuous testing
-
-Continuous testing only detects test classes annotated with `@QuarkusTest`, or
with annotations declared in the test sources that are meta-annotated with
`@QuarkusTest`.
-To run `@CamelQuarkusTest` classes in continuous testing, annotate them with
`@QuarkusTest` as well.
-
-[source,java]
-----
-@QuarkusTest
-@CamelQuarkusTest(routes = GreetingTest.Routes.class)
-class GreetingTest {
-}
-----
-
-=== Migrating from `CamelQuarkusTestSupport`
-
-[cols="1,1"]
-|===
-| `CamelQuarkusTestSupport` | `@CamelQuarkusTest`
-
-| `createRouteBuilder()`
-| `routes`. The routes apply to the whole test class. Test methods that need
different routes belong in separate test classes.
-
-| `isMockEndpoints()`, `isMockEndpointsAndSkip()`
-| `mockEndpoints`, `mockEndpointsAndSkip`. These require
<<_shared_mode_and_isolated_mode,isolated mode>>, including when only the
routes declared with `routes` are mocked.
-
-| `adviceRoute()`
-| `CamelTestHelper.adviceRoute()` or `advices`
-
-| `getMockEndpoint()`
-| `@EndpointInject` or `CamelTestHelper.getMockEndpoint()`
-
-| `@BindToRegistry`, `bindToRegistry()`
-| `@ReplaceInRegistry` or CDI producers
-
-| `isDumpRouteCoverage()`
-| `dumpRouteCoverage`
-
-| `doBeforeEach()`, `doAfterEach()`, `doAfterConstruct()`, `doAfterAll()`
-| JUnit `@BeforeEach`, `@AfterEach`, `@BeforeAll`, `@AfterAll`
-|===
-
-== `CamelTestSupport` style of testing
-
-If you used Camel standalone or on other runtimes before, you may know
`CamelTestSupport` already.
-The original `CamelTestSupport` class is not well suited to Quarkus, so
there's an extended Quarkus friendly version called `CamelQuarkusTestSupport`.
-
-> IMPORTANT: `CamelQuarkusTestSupport` only works in JVM mode. If you need to
test in native mode, then use one of the alternate test strategies described
above.
-
-To use `CamelQuarkusTestSupport`, you must add `camel-quarkus-junit` as a test
scoped dependency to your application.
-
-[source,xml]
-----
-<dependency>
- <groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-junit</artifactId>
- <scope>test</scope>
-</dependency>
-----
-
-=== Customizing the `CamelContext` for testing
-
-You can customize the `CamelContext` for testing with
https://quarkus.io/guides/config-reference#profiles[configuration profiles],
CDI beans, observers,
https://quarkus.io/guides/getting-started-testing#mock-support[mocks] etc.
-You can also override the `createCamelContext` method and interact directly
with the `CamelContext`.
-
-IMPORTANT: When using `createCamelContext` you *MUST NOT* instantiate and
return a new `CamelContext`. Instead, invoke `super.createCamelContext()` and
modify the returned `CamelContext` as needed.
-Failing to follow this rule will result in an exception being thrown.
-
-[source,java]
-----
-@QuarkusTest
-class SimpleTest extends CamelQuarkusTestSupport {
-
- @Override
- protected CamelContext createCamelContext() throws Exception {
- // Must call super to get a handle on the application scoped
CamelContext
- CamelContext context = super.createCamelContext();
- // Apply customizations
- context.setTracing(true);
- // Return the modified CamelContext
- return context;
- }
-}
-----
-
-=== Configuring routes for testing
-
-Any classes that extend `RouteBuilder` in your application will have their
routes automatically added to the `CamelContext`.
-Similarly, any XML or YAML routes configured from
`camel.main.routes-include-pattern` will also be loaded.
-
-This may not always be desirable for your tests.
-You control which routes get loaded at test time with configuration properties:
-
-* `quarkus.camel.routes-discovery.include-patterns`
-* `quarkus.camel.routes-discovery.exclude-patterns`,
-* `camel.main.routes-include-pattern`
-* `camel.main.routes-exclude-pattern`.
-
-You can also define test specific routes per test class by overriding
`createRouteBuilder`:
-
-[source,java]
-----
-@QuarkusTest
-class SimpleTest extends CamelQuarkusTestSupport {
- @Test
- void testGreeting() {
- MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
- mockEndpoint.expectedBodiesReceived("Hello World");
-
- template.sendBody("direct:start", "World");
-
- mockEndpoint.assertIsSatisfied();
- }
-
- @Override
- protected RoutesBuilder createRouteBuilder() throws Exception {
- return new RouteBuilder() {
- @Override
- public void configure() throws Exception {
- from("direct:start")
- .transform().simple("Hello ${body}")
- .to("mock:result");
- }
- };
- }
-}
-----
-
-=== CamelContext test lifecycle
-
-One of the main differences in `CamelQuarkusTestSupport` compared to
`CamelTestSupport` is how the `CamelContext` lifecycle is managed.
-
-On Camel Quarkus, a single `CamelContext` is created for you automatically by
the runtime.
-By default, this `CamelContext` is shared among all tests and remains started
for the duration of the entire test suite execution.
-
-This can potentially have some unintended side effects for your tests.
-If you need to have the `CamelContext` restarted between tests, then you can
create a custom
https://quarkus.io/guides/getting-started-testing#testing_different_profiles[test
profile], which will force the application under test to be restarted.
-
-For example, to define a test profile:
-
-[source,java]
-----
-@QuarkusTest
-class MyTestProfile implements QuarkusTestProfile {
- ...
-}
-----
-
-Then reference it on the test class with `@TestProfile`:
-
-[source,java]
-----
-// @TestProfile will trigger the application to be restarted
-@TestProfile(MyTestProfile.class)
-@QuarkusTest
-class SimpleTest extends CamelQuarkusTestSupport {
- ...
-}
-----
-
-NOTE: You cannot manually restart the `CamelContext` by invoking its `stop()`
and `start()` methods. This will result in an exception.
-
-=== Examples
-
-==== Simple `RouteBuilder` and test class
-
-Simple `RouteBuilder`:
-
-[source,java]
-----
-public class MyRoutes extends RouteBuilder {
- @Override
- public void configure() {
- from("direct:start")
- .transform().simple("Hello ${body}")
- .to("mock:result");
- }
-}
-----
-
-Test sending a message payload to the `direct:start` endpoint:
-
-[source,java]
-----
-@QuarkusTest
-class SimpleTest extends CamelQuarkusTestSupport {
- @Test
- void testGreeting() {
- MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
- mockEndpoint.expectedBodiesReceived("Hello World");
-
- template.sendBody("direct:start", "World");
-
- mockEndpoint.assertIsSatisfied();
- }
-}
-----
-
-==== 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");
- });
- // Routes that aren't advised are automatically started after this
method completes
- }
-
- @Test
- void testAdvisedRoute() throws Exception {
- MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
- mockEndpoint.expectedBodiesReceived("Hello World");
-
- template.sendBody("direct:replaced", "World");
-
- mockEndpoint.assertIsSatisfied();
- }
-}
-----
-
-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:
-
-* 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
-
-==== Test lifecycle methods inherited from `CamelTestSupport`
-
-`CamelQuarkusTestSupport` inherits some test lifecycle methods from
`CamelTestSupport`. However, they should not be used and instead are replaced
with equivalent methods in `CamelQuarkusTestSupport`.
-
-|===
-|CamelTestSupport lifecycle methods |CamelQuarkusTestSupport equivalent
-
-|`afterAll`
-|`doAfterAll`
-
-|`afterEach`, `afterTestExecution`
-|`doAfterEach`
-
-|`beforeAll`
-|`doAfterConstruct`
-
-|`beforeEach`
-|`doBeforeEach`
-|===
-
-==== Creating a custom Camel registry is not supported
-
-The `CamelQuarkusTestSupport` implementation of `createCamelRegistry` will
throw `UnsupportedOperationException`.
-
-If you need to bind or unbind objects to the Camel registry, then you can do
it by one of the following methods.
-
-* Produce named CDI beans
-+
-[source,java]
-----
-public class MyBeanProducers {
- @Produces
- @Named("myBean")
- public MyBean createMyBean() {
- return new MyBean();
- }
-}
-----
-+
-* Override `createCamelContext` (see example above) and invoke
`camelContext.getRegistry().bind("foo", fooBean)`
-* Use the `@BindToRegistry` annotation
-+
-[source,java]
-----
-@QuarkusTest
-class SimpleTest extends CamelQuarkusTestSupport {
- @BindToRegistry("myBean")
- MyBean myBean = new MyBean();
-}
-----
-+
-+
-
-NOTE: Beans bound to the Camel registry from individual test classes, will
persist for the duration of the test suite execution.
-This could have unintended consequences, depending on your test expectations.
You can use test profiles to restart the `CamelContext` to avoid this.
diff --git
a/docs/modules/ROOT/pages/user-guide/testing/camel-quarkus-test-support.adoc
b/docs/modules/ROOT/pages/user-guide/testing/camel-quarkus-test-support.adoc
new file mode 100644
index 00000000000..6f11063b720
--- /dev/null
+++ b/docs/modules/ROOT/pages/user-guide/testing/camel-quarkus-test-support.adoc
@@ -0,0 +1,364 @@
+= `CamelTestSupport` style of testing
+
+If you used Camel standalone or on other runtimes before, you may know
`CamelTestSupport` already.
+The original `CamelTestSupport` class is not well suited to Quarkus, so
there's an extended Quarkus friendly version called `CamelQuarkusTestSupport`.
+
+> IMPORTANT: `CamelQuarkusTestSupport` only works in JVM mode. If you need to
test in native mode, then use one of the alternate test strategies described in
the xref:user-guide/testing.adoc[Testing] guide.
+
+To use `CamelQuarkusTestSupport`, you must add `camel-quarkus-junit` as a test
scoped dependency to your application.
+
+[source,xml]
+----
+<dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-junit</artifactId>
+ <scope>test</scope>
+</dependency>
+----
+
+== Customizing the `CamelContext` for testing
+
+You can customize the `CamelContext` for testing with
https://quarkus.io/guides/config-reference#profiles[configuration profiles],
CDI beans, observers,
https://quarkus.io/guides/getting-started-testing#mock-support[mocks] etc.
+You can also override the `createCamelContext` method and interact directly
with the `CamelContext`.
+
+IMPORTANT: When using `createCamelContext` you *MUST NOT* instantiate and
return a new `CamelContext`. Instead, invoke `super.createCamelContext()` and
modify the returned `CamelContext` as needed.
+Failing to follow this rule will result in an exception being thrown.
+
+[source,java]
+----
+@QuarkusTest
+class SimpleTest extends CamelQuarkusTestSupport {
+
+ @Override
+ protected CamelContext createCamelContext() throws Exception {
+ // Must call super to get a handle on the application scoped
CamelContext
+ CamelContext context = super.createCamelContext();
+ // Apply customizations
+ context.setTracing(true);
+ // Return the modified CamelContext
+ return context;
+ }
+}
+----
+
+== Configuring routes for testing
+
+Any classes that extend `RouteBuilder` in your application will have their
routes automatically added to the `CamelContext`.
+Similarly, any XML or YAML routes configured from
`camel.main.routes-include-pattern` will also be loaded.
+
+This may not always be desirable for your tests.
+You control which routes get loaded at test time with configuration properties:
+
+* `quarkus.camel.routes-discovery.include-patterns`
+* `quarkus.camel.routes-discovery.exclude-patterns`,
+* `camel.main.routes-include-pattern`
+* `camel.main.routes-exclude-pattern`.
+
+You can also define test specific routes per test class by overriding
`createRouteBuilder`:
+
+[source,java]
+----
+@QuarkusTest
+class SimpleTest extends CamelQuarkusTestSupport {
+ @Test
+ void testGreeting() {
+ MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
+ mockEndpoint.expectedBodiesReceived("Hello World");
+
+ template.sendBody("direct:start", "World");
+
+ mockEndpoint.assertIsSatisfied();
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .transform().simple("Hello ${body}")
+ .to("mock:result");
+ }
+ };
+ }
+}
+----
+
+== CamelContext test lifecycle
+
+One of the main differences in `CamelQuarkusTestSupport` compared to
`CamelTestSupport` is how the `CamelContext` lifecycle is managed.
+
+On Camel Quarkus, a single `CamelContext` is created for you automatically by
the runtime.
+By default, this `CamelContext` is shared among all tests and remains started
for the duration of the entire test suite execution.
+
+This can potentially have some unintended side effects for your tests.
+If you need to have the `CamelContext` restarted between tests, then you can
create a custom
https://quarkus.io/guides/getting-started-testing#testing_different_profiles[test
profile], which will force the application under test to be restarted.
+
+For example, to define a test profile:
+
+[source,java]
+----
+@QuarkusTest
+class MyTestProfile implements QuarkusTestProfile {
+ ...
+}
+----
+
+Then reference it on the test class with `@TestProfile`:
+
+[source,java]
+----
+// @TestProfile will trigger the application to be restarted
+@TestProfile(MyTestProfile.class)
+@QuarkusTest
+class SimpleTest extends CamelQuarkusTestSupport {
+ ...
+}
+----
+
+NOTE: You cannot manually restart the `CamelContext` by invoking its `stop()`
and `start()` methods. This will result in an exception.
+
+== Examples
+
+=== Simple `RouteBuilder` and test class
+
+Simple `RouteBuilder`:
+
+[source,java]
+----
+public class MyRoutes extends RouteBuilder {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .transform().simple("Hello ${body}")
+ .to("mock:result");
+ }
+}
+----
+
+Test sending a message payload to the `direct:start` endpoint:
+
+[source,java]
+----
+@QuarkusTest
+class SimpleTest extends CamelQuarkusTestSupport {
+ @Test
+ void testGreeting() {
+ MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
+ mockEndpoint.expectedBodiesReceived("Hello World");
+
+ template.sendBody("direct:start", "World");
+
+ mockEndpoint.assertIsSatisfied();
+ }
+}
+----
+
+=== 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");
+ });
+ // Routes that aren't advised are automatically started after this
method completes
+ }
+
+ @Test
+ void testAdvisedRoute() throws Exception {
+ MockEndpoint mockEndpoint = getMockEndpoint("mock:result");
+ mockEndpoint.expectedBodiesReceived("Hello World");
+
+ template.sendBody("direct:replaced", "World");
+
+ mockEndpoint.assertIsSatisfied();
+ }
+}
+----
+
+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:
+
+* 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
+
+=== Test lifecycle methods inherited from `CamelTestSupport`
+
+`CamelQuarkusTestSupport` inherits some test lifecycle methods from
`CamelTestSupport`. However, they should not be used and instead are replaced
with equivalent methods in `CamelQuarkusTestSupport`.
+
+|===
+|CamelTestSupport lifecycle methods |CamelQuarkusTestSupport equivalent
+
+|`afterAll`
+|`doAfterAll`
+
+|`afterEach`, `afterTestExecution`
+|`doAfterEach`
+
+|`beforeAll`
+|`doAfterConstruct`
+
+|`beforeEach`
+|`doBeforeEach`
+|===
+
+=== Creating a custom Camel registry is not supported
+
+The `CamelQuarkusTestSupport` implementation of `createCamelRegistry` will
throw `UnsupportedOperationException`.
+
+If you need to bind or unbind objects to the Camel registry, then you can do
it by one of the following methods.
+
+* Produce named CDI beans
++
+[source,java]
+----
+public class MyBeanProducers {
+ @Produces
+ @Named("myBean")
+ public MyBean createMyBean() {
+ return new MyBean();
+ }
+}
+----
++
+* Override `createCamelContext` (see example above) and invoke
`camelContext.getRegistry().bind("foo", fooBean)`
+* Use the `@BindToRegistry` annotation
++
+[source,java]
+----
+@QuarkusTest
+class SimpleTest extends CamelQuarkusTestSupport {
+ @BindToRegistry("myBean")
+ MyBean myBean = new MyBean();
+}
+----
++
++
+
+NOTE: Beans bound to the Camel registry from individual test classes, will
persist for the duration of the test suite execution.
+This could have unintended consequences, depending on your test expectations.
You can use test profiles to restart the `CamelContext` to avoid this.
diff --git a/docs/modules/ROOT/pages/user-guide/testing/camel-quarkus-test.adoc
b/docs/modules/ROOT/pages/user-guide/testing/camel-quarkus-test.adoc
new file mode 100644
index 00000000000..5ad74966b0f
--- /dev/null
+++ b/docs/modules/ROOT/pages/user-guide/testing/camel-quarkus-test.adoc
@@ -0,0 +1,209 @@
+= Annotation based testing with `@CamelQuarkusTest`
+
+`@CamelQuarkusTest` configures Camel specific test features with annotation
attributes, without extending a base class.
+It implies `@QuarkusTest`.
+
+IMPORTANT: `@CamelQuarkusTest` only works in JVM mode.
+
+To use `@CamelQuarkusTest`, add `camel-quarkus-junit` as a test scoped
dependency to your application.
+
+[source,xml]
+----
+<dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-junit</artifactId>
+ <scope>test</scope>
+</dependency>
+----
+
+Everything declared on `@CamelQuarkusTest` applies to the whole test class.
+It is set up before the first test method runs and undone after the last one.
+`MockEndpoint` instances are reset after each test method.
+
+== Test routes
+
+Routes for a test class are declared with `routes`.
+They are added before the first test method and removed after the last one.
+Route builder classes declared in a `@CamelQuarkusTest` class, or listed in
`routes`, are never discovered as application routes.
+
+`CamelContext`, `ProducerTemplate`, `FluentProducerTemplate` and
`ConsumerTemplate` can be injected with `@Inject`, and endpoints with
`@EndpointInject`.
+
+[source,java]
+----
+@CamelQuarkusTest(routes = GreetingTest.Routes.class)
+class GreetingTest {
+
+ @Inject
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint result;
+
+ @Test
+ void greets() throws Exception {
+ result.expectedBodiesReceived("Hello World");
+ template.sendBody("direct:start", "World");
+ result.assertIsSatisfied();
+ }
+
+ public static class Routes extends RouteBuilder {
+ @Override
+ public void configure() {
+ from("direct:start").transform().simple("Hello
${body}").to("mock:result");
+ }
+ }
+}
+----
+
+== Advising application routes
+
+Advice for the test class is declared with `advices`, and route consumers are
replaced with `replaceRouteFromWith`.
+
+[source,java]
+----
+@CamelQuarkusTest(
+ advices = @AdviceRoute(route = "my-kafka-route", advice =
KafkaRouteTest.StubKafka.class),
+ replaceRouteFromWith = "my-timer-route=direct:trigger")
+class KafkaRouteTest {
+
+ static class StubKafka extends AdviceWithRouteBuilder {
+ @Override
+ public void configure() {
+
weaveByToUri("kafka:output-topic*").replace().to("mock:kafka-stub");
+ }
+ }
+}
+----
+
+Advice for a single test method is applied with `CamelTestHelper`, which can
be injected into the test class.
+It is undone after the test method.
+
+[source,java]
+----
+@CamelQuarkusTest
+class MyRouteTest {
+
+ @Inject
+ CamelTestHelper helper;
+
+ @Test
+ void withMock() throws Exception {
+ helper.adviceRoute("my-route", route ->
route.weaveByToUri("kafka:*").replace().to("mock:kafka"));
+ // ...
+ }
+}
+----
+
+`CamelTestHelper` also provides `getMockEndpoint`, `resetMocks` and
`startRouteDefinitions`.
+
+== Replacing beans in the Camel registry
+
+Fields and no-arg methods annotated with `@ReplaceInRegistry` are bound to the
Camel registry, using the member name as the bean name.
+The previous bindings are restored after the last test method.
+The beans are bound before the routes declared with `routes` are added.
Application routes already exist at that point, as do the routes declared with
`routes` in <<_shared_mode_and_isolated_mode,isolated mode>>, so routes that
look up a bean when they are created do not use the replacement.
+Beans provided by CDI cannot be replaced this way. Use
https://quarkus.io/guides/getting-started-testing#mock-support[`@InjectMock` or
`QuarkusMock`] for those.
+
+[source,java]
+----
+@CamelQuarkusTest
+class GreeterTest {
+
+ @ReplaceInRegistry
+ Greeter greeter = new FakeGreeter();
+}
+----
+
+== Shared mode and isolated mode
+
+By default, a test class shares the application with the other test classes of
the module, and its routes are already started when the test class begins.
+Some features need to act before routes are created. They are only available
when the test class owns its application, which is called isolated mode.
+
+A test class runs in isolated mode when it is annotated with `@TestProfile`
and no other `@CamelQuarkusTest` class uses the same test profile.
+The usual way is for the test class to be its own test profile, in which case
it must be public.
+
+[source,java]
+----
+@CamelQuarkusTest(mockEndpointsAndSkip = "kafka:*")
+@TestProfile(OrdersTest.class)
+public class OrdersTest implements QuarkusTestProfile {
+}
+----
+
+Using a feature that is only available in isolated mode fails the test build.
+
+[cols="2,1,1"]
+|===
+| Feature | Shared mode | Isolated mode
+
+| `routes`, `advices`, `replaceRouteFromWith`, `@ReplaceInRegistry`
+| Yes
+| Yes
+
+| `mockEndpoints`, `mockEndpointsAndSkip`, `stubEndpoints`
+| No
+| Yes
+
+| `autoStartupExclude`
+| No
+| Yes
+
+| `@ProvidesBreakpoint`
+| No
+| Yes
+
+| `dumpRouteCoverage`, `dumpRoute`, `shutdownTimeout`
+| Yes
+| Yes
+|===
+
+In isolated mode, `advices` and `replaceRouteFromWith` are applied before
routes are created.
+
+== Debugging, route coverage and route dumps
+
+A `public static` no-arg method annotated with `@ProvidesBreakpoint` and
returning `org.apache.camel.spi.Breakpoint` enables the Camel debugger with
that breakpoint.
+
+`dumpRouteCoverage = true` writes route coverage to
`target/camel-route-coverage` after each test method. It requires
`camel-quarkus-management`.
+
+`dumpRoute` writes the routes to `target/camel-route-dump` after each test
method, in `xml` or `yaml` format.
+It requires `camel-quarkus-xml-io-dsl` for `xml`, and `camel-quarkus-yaml-io`
for `yaml`.
+
+== Continuous testing
+
+Continuous testing only detects test classes annotated with `@QuarkusTest`, or
with annotations declared in the test sources that are meta-annotated with
`@QuarkusTest`.
+To run `@CamelQuarkusTest` classes in continuous testing, annotate them with
`@QuarkusTest` as well.
+
+[source,java]
+----
+@QuarkusTest
+@CamelQuarkusTest(routes = GreetingTest.Routes.class)
+class GreetingTest {
+}
+----
+
+== Migrating from `CamelQuarkusTestSupport`
+
+[cols="1,1"]
+|===
+| `CamelQuarkusTestSupport` | `@CamelQuarkusTest`
+
+| `createRouteBuilder()`
+| `routes`. The routes apply to the whole test class. Test methods that need
different routes belong in separate test classes.
+
+| `isMockEndpoints()`, `isMockEndpointsAndSkip()`
+| `mockEndpoints`, `mockEndpointsAndSkip`. These require
<<_shared_mode_and_isolated_mode,isolated mode>>, including when only the
routes declared with `routes` are mocked.
+
+| `adviceRoute()`
+| `CamelTestHelper.adviceRoute()` or `advices`
+
+| `getMockEndpoint()`
+| `@EndpointInject` or `CamelTestHelper.getMockEndpoint()`
+
+| `@BindToRegistry`, `bindToRegistry()`
+| `@ReplaceInRegistry` or CDI producers
+
+| `isDumpRouteCoverage()`
+| `dumpRouteCoverage`
+
+| `doBeforeEach()`, `doAfterEach()`, `doAfterConstruct()`, `doAfterAll()`
+| JUnit `@BeforeEach`, `@AfterEach`, `@BeforeAll`, `@AfterAll`
+|===
diff --git a/docs/modules/ROOT/pages/user-guide/testing/external-services.adoc
b/docs/modules/ROOT/pages/user-guide/testing/external-services.adoc
new file mode 100644
index 00000000000..12aca837917
--- /dev/null
+++ b/docs/modules/ROOT/pages/user-guide/testing/external-services.adoc
@@ -0,0 +1,152 @@
+= Testing with external services
+
+== Testcontainers
+
+Sometimes your application needs to access an external service such as a
message broker, database, etc.
+If a container image is available for the service of interest,
https://www.testcontainers.org/[Testcontainers]
+can be used to start and stop these services for testing.
+
+For the application to work properly, it is often essential to pass the
connection configuration data
+(host, port, user, password, etc. of the remote service) to the application
before it starts.
+In the Quarkus ecosystem, `QuarkusTestResourceLifecycleManager` serves this
purpose.
+
+You can start one or more containers in the `start()` method
+and return the connection configuration in the form of a `Map`, where the key
is the name of a configuration property and the value is the property value.
+The entries of this map are then passed to the application either via command
line (`-Dkey=value`) in native mode,
+or through a special MicroProfile configuration provider in JVM mode.
+Note that these settings have a higher precedence than the settings in
`application.properties` file.
+
+[source,java]
+----
+import java.util.Map;
+import java.util.HashMap;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+public class MyTestResource implements QuarkusTestResourceLifecycleManager {
+
+ private GenericContainer<?> myContainer;
+
+ @Override
+ public Map<String, String> start() {
+ // Start the needed container(s)
+ myContainer = new
GenericContainer(DockerImageName.parse("my/image:1.0.0"))
+ .withExposedPorts(1234)
+ .waitingFor(Wait.forListeningPort());
+
+ myContainer.start();
+
+ // Pass the configuration to the application under test
+ // You can also pass camel component property names / values to
automatically configure Camel components
+ return new HashMap<>() {{
+ put("my-container.host", container.getHost());
+ put("my-container.port", "" + container.getMappedPort(1234));
+ }};
+ }
+
+ @Override
+ public void stop() {
+ // Stop the needed container(s)
+ myContainer.stop();
+ }
+}
+----
+
+The defined test resource needs to be referenced from the test classes with
`@QuarkusTestResource` as shown below:
+
+[source,java]
+----
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+@QuarkusTestResource(MyTestResource.class)
+class MyTest {
+ ...
+}
+----
+
+More information can be found in the
https://quarkus.io/guides/getting-started-testing#quarkus-test-resource[Quarkus
testing guide]. Refer to Camel Quarkus source tree for a
https://github.com/apache/camel-quarkus/blob/main/integration-tests/google-storage/src/test/java/org/apache/camel/quarkus/component/google/storage/it/GoogleStorageTestResource.java[complete
example].
+
+== WireMock
+
+It is sometimes useful to stub HTTP interactions with third party services &
APIs so that tests do not have to connect to live endpoints, as this can incur
costs and the service may not always be 100% available or reliable.
+
+An excellent tool for mocking & recording HTTP interactions is
http://wiremock.org/[WireMock]. It is used extensively throughout the Camel
Quarkus test suite for various component extensions. Here follows a typical
workflow
+for setting up WireMock.
+
+First set up the WireMock server. Note that it is important to configure the
Camel component under test to pass any HTTP interactions through the WireMock
proxy. This is usually achieved by configuring a component property
+that determines the API endpoint URL. Sometimes things are less
straightforward and some extra work is required to configure the API client
library, as was the case for
https://github.com/apache/camel-quarkus/blob/main/integration-tests/twilio/src/main/java/org/apache/camel/quarkus/component/twilio/it/TwilioResource.java#L83[Twilio].
+
+[source,java]
+----
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static
com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+
+public class WireMockTestResource implements
QuarkusTestResourceLifecycleManager {
+
+ private WireMockServer server;
+
+ @Override
+ public Map<String, String> start() {
+ // Setup & start the server
+ server = new WireMockServer(
+ wireMockConfig().dynamicPort()
+ );
+ server.start();
+
+ // Stub an HTTP endpoint. Note that WireMock also supports a record
and playback mode
+ // https://wiremock.org/docs/record-playback/
+ server.stubFor(
+ get(urlEqualTo("/api/greeting"))
+ .willReturn(aResponse()
+ .withHeader("Content-Type", "application/json")
+ .withBody("{\"message\": \"Hello World\"}")));
+
+ // Ensure the camel component API client passes requests through the
WireMock proxy
+ Map<String, String> conf = new HashMap<>();
+ conf.put("camel.component.foo.server-url", server.baseUrl());
+ return conf;
+ }
+
+ @Override
+ public void stop() {
+ if (server != null) {
+ server.stop();
+ }
+ }
+}
+----
+
+Finally, ensure your test class has the `@QuarkusTestResource` annotation with
the appropriate test resource class specified as the value. The WireMock server
will be started before all tests are
+executed and will be shut down when all tests are finished.
+
+[source,java]
+----
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+@QuarkusTestResource(WireMockTestResource.class)
+class MyTest {
+ ...
+}
+----
+
+More examples of WireMock usage can be found in the Camel Quarkus integration
test source tree such as in the
https://github.com/apache/camel-quarkus/blob/main/integration-tests/validator/src/test/java/org/apache/camel/quarkus/component/validator/it/ValidatorTestResource.java[validator
tests].
+
+== Citrus Testing Framework
+
+The https://citrusframework.org/[Citrus Testing Framework] provides advanced
integration testing capabilities for Camel Quarkus applications.
+For detailed information about using Citrus with Quarkus test support, see
xref:manual::integration-test.adoc#_quarkus_test_support[Integration Testing
with Citrus] in the Apache Camel documentation.