Chapter 1. Getting Started with Red Hat build of Apache Camel for Quarkus This guide introduces Red Hat build of Apache Camel for Quarkus, the various ways to create a project and how to get started building an application using Red Hat build of Apache Camel for Quarkus: 1.1. Red Hat build of Apache Camel for Quarkus overview Red Hat build of Apache Camel for Quarkus brings the integration capabilities of Apache Camel and its vast component library to the Quarkus runtime. The benefits of using Red Hat build of Apache Camel for Quarkus include the following: Enables users to take advantage of the performance benefits, developer joy and the container first ethos which Quarkus provides. Provides Quarkus extensions for many of the Apache Camel components. Takes advantage of the many performance improvements made in Camel, which results in a lower memory footprint, less reliance on reflection and faster startup times. 1.1.1. Languages In Red Hat build of Apache Camel for Quarkus, you can define Camel routes using the following languages: Java DSL YAML XML IO 1.2. Tooling 1.2.1. IDE plugins Quarkus has plugins for most of the popular development IDEs which provide Quarkus language support, code/configuration completion, project creation wizards and much more. The plugins are available at each respective IDE marketplace. VS Code extension Eclipse plugin (currently not supported) IntelliJ plugin (currently not supported) Check the plugin documentation to discover how to create projects for your preferred IDE. 1.2.2. Camel content assist The following plugins provide support for content assist when editing Camel routes and application.properties : VS Code Language support for Camel - a part of the Camel extension pack Debug Adapter for Apache Camel to debug Camel integrations written in Java, YAML or XML locally. For more information about scope of development support, see Development Support Scope of Coverage Eclipse Desktop Language Support for Camel - a part of Jboss Tools Apache Camel IDEA plugin (not always up to date) Users of other IDEs supporting Language Server Protocol may choose to install and configure Camel Language Server manually 1.3. Building your first project with Red Hat build of Apache Camel for Quarkus 1.3.1. Overview You can use code.quarkus.redhat.com to generate a Quarkus Maven project which automatically adds and configures the extensions that you want to use in your application. This section walks you through the process of creating a Quarkus Maven project with Red Hat build of Apache Camel for Quarkus including: Creating the skeleton application using code.quarkus.redhat.com Adding a simple Camel route Exploring the application code Compiling the application in development mode Testing the application 1.3.2. Generating the skeleton application with code.quarkus.redhat.com You can bootstrap and generate projects on code.quarkus.redhat.com . The Red Hat build of Apache Camel for Quarkus extensions are located under the 'Integration' heading. If you need additional extensions, use the 'search' field to find them. Select the component extensions that you want to work with and click 'Generate your application' to download a basic skeleton project. You can also push the project directly to GitHub. For more information about using code.quarkus.redhat.com to generate Quarkus Maven projects, see Creating a Quarkus Maven project using code.quarkus.redhat.com in the Getting started with Red Hat build of Quarkus guide. Procedure In the code.quarkus.redhat.com website, select the following extensions: camel-quarkus-rest camel-quarkus-jackson camel-quarkus-direct Note Do not compile the application on code.quarkus.redhat.com (in the final step of the procedure). Instead, use the compile command described in the Section 1.3.5, "Development mode" section below. Navigate to the directory where you extracted the generated project files from the step: USD cd <directory_name> 1.3.3. Explore the application code The application has two compile dependencies which are managed within the com.redhat.quarkus.platform:quarkus-camel-bom that is imported in <dependencyManagement> .: pom.xml <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>com.redhat.quarkus.platform</quarkus.platform.group-id> <quarkus.platform.version> <!-- The latest 3.15.x version from https://maven.repository.redhat.com/ga/com/redhat/quarkus/platform/quarkus-bom --> </quarkus.platform.version> ... <dependency> <groupId>USD{quarkus.platform.group-id}</groupId> <artifactId>USD{quarkus.platform.artifact-id}</artifactId> <version>USD{quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>USD{quarkus.platform.group-id}</groupId> <artifactId>quarkus-camel-bom</artifactId> <version>USD{quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> Note For more information about BOM dependency management, see Developing Applications with Red Hat build of Apache Camel for Quarkus The application is configured by properties defined within src/main/resources/application.properties , for example, the camel.context.name can be set there. 1.3.4. Adding a simple Camel route Procedure Create a file named Routes.java in the src/main/java/org/acme/ subfolder. Add a Camel Rest route as shown in the following code snippet: Routes.java package org.acme.rest.json; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.model.rest.RestBindingMode; /** * Camel route definitions. */ public class Routes extends RouteBuilder { private final Set<Fruit> fruits = Collections.synchronizedSet(new LinkedHashSet<>()); private final Set<Legume> legumes = Collections.synchronizedSet(new LinkedHashSet<>()); public Routes() { /* Let's add some initial fruits */ this.fruits.add(new Fruit("Apple", "Winter fruit")); this.fruits.add(new Fruit("Pineapple", "Tropical fruit")); /* Let's add some initial legumes */ this.legumes.add(new Legume("Carrot", "Root vegetable, usually orange")); this.legumes.add(new Legume("Zucchini", "Summer squash")); } @Override public void configure() throws Exception { restConfiguration().bindingMode(RestBindingMode.json); rest("/fruits") .get() .to("direct:getFruits") .post() .type(Fruit.class) .to("direct:addFruit"); rest("/legumes") .get() .to("direct:getLegumes"); from("direct:getFruits") .setBody().constant(fruits); from("direct:addFruit") .process().body(Fruit.class, fruits::add) .setBody().constant(fruits); from("direct:getLegumes") .setBody().constant(legumes); } } For more information about this example, see camel-quarkus-examples repository . 1.3.5. Development mode USD mvn clean compile quarkus:dev This command compiles the project, starts your application, and lets the Quarkus tooling watch for changes in your workspace. Any modifications you make to your project will automatically take effect in the running application. You can check the application in your browser. (For example, for the rest-json sample application, access http://localhost:8080/fruits ) If you change the application code, for example, change 'Apple' to 'Orange', your application automatically updates. To see the changes applied, refresh your browser. Refer to Quarkus documentation Development mode section for more details about the development mode. 1.3.6. Testing 1.3.6.1. JVM mode To test the Camel Rest route that we have created in JVM mode, add a test class as follows: Procedure Create a file named RoutesTest.java in the src/test/java/org/acme/ subfolder. Add the RoutesTest class as shown in the following code snippet: RoutesTest.java package org.acme; import io.quarkus.test.junit.QuarkusTest; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; import org.hamcrest.Matchers; @QuarkusTest public class RoutesTest { @Test public void testFruitsEndpoint() { /* Assert the initial fruit is there */ given() .when().get("/fruits") .then() .statusCode(200) .body( "USD.size()", Matchers.is(1), "name", Matchers.contains("Orange")); /* Add a new fruit */ given() .body("{\"name\": \"Pear\"}") .header("Content-Type", "application/json") .when() .post("/fruits") .then() .statusCode(200); /* Assert that pear was added */ given() .when().get("/fruits") .then() .statusCode(200) .body( "USD.size()", Matchers.is(2), "name", Matchers.contains("Orange", "Pear")); } } The JVM mode tests are run by maven-surefire-plugin in the test Maven phase: USD mvn clean test 1.3.6.2. Native mode To test the Camel Rest route that we have created in Native mode, add a test class as follows: Procedure Create a file named NativeRoutesIT.java in the src/test/java/org/acme/ subfolder. Add the NativeRoutesIT class as shown in the following code snippet: NativeRoutesIT.java package org.acme; import io.quarkus.test.junit.NativeImageTest; @NativeImageTest public class NativeRoutesIT extends RoutesTest { // Execute the same tests but in native mode. } The native mode tests are verified by maven-failsafe-plugin in the verify phase. Pass the native property to activate the profile that runs them: USD mvn clean verify -Pnative Tip For more details, and how to use the CamelTestSupport style of testing, see Testing Camel Quarkus Extensions . 1.3.7. Packaging and running the application 1.3.7.1. JVM mode Procedure Run mvn package to prepare a thin jar for running on a stock JVM: USD mvn clean package USD ls -lh target/quarkus-app ... -rw-r--r--. 1 user user 238K Oct 11 18:55 quarkus-run.jar ... Note The thin jar contains just the application code. You also need the dependencies in target/quarkus-app/lib to run it. Run the jar as follows: USD java -jar target/quarkus-app/quarkus-run.jar ... [io.quarkus] (main) Quarkus started in 1.163s. Listening on: http://[::]:8080 Note The boot time should be around a second. 1.3.7.2. Native mode Procedure To prepare a native executable, do as follows: Run the command mvn clean package -Pnative : USD mvn clean package -Pnative USD ls -lh target ... -rwxr-xr-x. 1 user user 46M Oct 11 18:57 code-with-quarkus-1.0.0-SNAPSHOT-runner ... Note The runner has no .jar extension and has the x (executable) permission set. You can run it directly: USD ./target/*-runner ... [io.quarkus] (main) Quarkus started in 0.013s. Listening on: http://[::]:8080 ... The application started in 13 milliseconds. View the memory usage with the ps -o rss,command -p USD(pgrep code-with) command : USD ps -o rss,command -p USD(pgrep code-with) RSS COMMAND 65852 ./target/code-with-quarkus-1.0.0-SNAPSHOT-runner The application uses 65 MB of memory. Tip See Producing a native executable in the Compiling your Quarkus applications to native executables guide for additional information about preparing a native executable. Tip Quarkus Native executable guide contains more details, including steps for creating a container image . 1.4. Testing Camel Quarkus Extensions Testing offers a good way to ensure Camel routes behave as expected over time. If you haven't already, read the Camel Quarkus user guide First Steps and the Quarkus documentation Testing your application section. 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. In JVM mode, you can use the CamelTestSupport style of testing . 1.4.1. Running in JVM mode In JVM mode, use the @QuarkusTest annotation to bootstrap Quarkus and start Camel routes before the @Test logic executes. For example: import io.quarkus.test.junit.QuarkusTest; import org.junit.jupiter.api.Test; @QuarkusTest class MyTest { @Test public void test() { // Use any suitable code that sends test data to the route and then assert outcomes ... } } Tip You can find a sample implementation in the Camel Quarkus source: MessageTest.java 1.4.2. Running in native mode Note Always test that your application works in native mode for all supported extensions. You can reuse the test logic defined for JVM mode by inheriting the logic from the respective JVM mode class. Add the @QuarkusIntegrationTest annotation to tell the Quarkus JUnit extension to compile the application under test to native image and start it before running the tests. import io.quarkus.test.junit.QuarkusIntegrationTest; @QuarkusIntegrationTest class MyIT extends MyTest { ... } Tip You can find a sample implementation in the Camel Quarkus source: MessageRecordIT.java 1.4.3. Differences between @QuarkusTest and @QuarkusIntegrationTest A native executable does not need a JVM to run, and cannot run in a JVM, because it is native code, not bytecode. There is no point in compiling tests to native code so they run using a traditional JVM. This means that communication between tests and the application must go over the network (HTTP/REST, or any other protocol your application speaks), through watching filesystems (log files for example), or any other interprocess communication. 1.4.3.1. @QuarkusTest in JVM mode In JVM mode, tests annotated with @QuarkusTest execute in the same JVM as the application under test. This means you can use @Inject to add beans from the application into the test code. You can also define new beans or even override the beans from the application using @jakarta.enterprise.inject.Alternative and @jakarta.annotation.Priority . 1.4.3.2. @QuarkusIntegrationTest in native mode In native mode, tests annotated with @QuarkusIntegrationTest execute in a JVM hosted in a process separate from the running native application. An important consequence of this, is that all communication between the tests and the native application, must take one or more of the following forms: Network calls. Typically, HTTP or any other network protocol your application supports. Watching the filesystem for changes. (For example via Camel file endpoints.) Any other kind of interprocess communication. QuarkusIntegrationTest provides additional features that are not available through @QuarkusTest : In JVM mode, you can launch and test the runnable application JAR produced by the Quarkus build. In native mode, you can launch and test the native application produced by the Quarkus build. If you add a container image to the build, a container starts, and tests execute against it. For more information about QuarkusIntegrationTest , see the Quarkus testing guide . 1.4.4. Testing with external services 1.4.4.1. Testcontainers Sometimes your application needs to access some external resource, such as a messaging broker, a database, or other service. If a container image is available for the service of interest, you can use Testcontainers to start and configure the services during testing. 1.4.4.1.1. Passing configuration data with QuarkusTestResourceLifecycleManager For the application to work properly, it is often essential to pass the connection configuration data (host, port, user, password of the remote service) to the application before it starts. In the Quarkus ecosystem, QuarkusTestResourceLifecycleManager serves this purpose. You can start one or more Testcontainers in the start() method and return the connection configuration from the method in the form of a Map . The entries of this map are then passed to the application in different ways depending on the mode: Native mode: a command line ( -Dkey=value ) JVM Mode: a special MicroProfile configuration provider Note Command line and MicroProfile settings have a higher precedence than the settings in the application.properties file. 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(); ... } } Reference the defined test resource from the test classes with @QuarkusTestResource : import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; @QuarkusTest @QuarkusTestResource(MyTestResource.class) class MyTest { ... } Tip You can find a sample implementation in the Camel Quarkus source: NatsTestResource.java 1.4.4.2. WireMock Instead of having the tests connect to live endpoints, for example, if they are unavailable, unreliable, or expensive, you can stub HTTP interactions with third-party services & APIs. You can use WireMock for mocking & recording HTTP interactions. It is used extensively throughout the Camel Quarkus test suite for various component extensions. 1.4.4.2.1. Setting up WireMock Procedure Set up the WireMock server. Note Always configure the Camel component under test to pass any HTTP interactions through the WireMock proxy. You can achieve this by configuring a component property that determines the API endpoint URL. 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. 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(); } } } 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. import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; @QuarkusTest @QuarkusTestResource(WireMockTestResource.class) class MyTest { ... } The WireMock server starts before all tests execute and shuts down when all tests finish. Tip You can find a sample implementation in the Camel Quarkus integration test source tree: Geocoder . 1.4.5. CamelTestSupport style of testing with CamelQuarkusTestSupport Since Camel Quarkus 2.13.0, you can use CamelQuarkusTestSupport for testing. It is a replacement for CamelTestSupport , which does not work well with Quarkus. 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. 1.4.5.1. Testing with CamelQuarkusTestSupport in JVM mode Add the following dependency into your module (preferably in the test scope): <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-junit5</artifactId> <scope>test</scope> </dependency> You can use CamelQuarkusTestSupport in your test like this: @QuarkusTest @TestProfile(SimpleTest.class) //necessary only if "newly created" context is required for the test (worse performance) public class SimpleTest extends CamelQuarkusTestSupport { ... } 1.4.5.2. Customizing the CamelContext for testing You can customize the CamelContext for testing with configuration profiles , CDI beans, observers, 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. @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; } } 1.4.5.3. 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 : @QuarkusTest class SimpleTest extends CamelQuarkusTestSupport { @Test void testGreeting() { MockEndpoint mockEndpoint = getMockEndpoint("mock:result"); mockEndpoint.expectedBodiesReceived("Hello World"); template.sendBody("direct:start", "World"); mockEndpoint.assertIsSatisified(); } @Override protected RoutesBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start") .transform().simple("Hello USD{body}") .to("mock:result"); } }; } } 1.4.5.4. 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 test profile , which will force the application under test to be restarted. For example, to define a test profile: @QuarkusTest class MyTestProfile implements QuarkusTestProfile { ... } Then reference it on the test class with @TestProfile : // @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. 1.4.5.5. Examples 1.4.5.5.1. Simple RouteBuilder and test class Simple RouteBuilder : public class MyRoutes extends RouteBuilder { @Override public void configure() { from("direct:start") .transform().simple("Hello USD{body}") .to("mock:result"); } } Test sending a message payload to the direct:start endpoint: @QuarkusTest class SimpleTest extends CamelQuarkusTestSupport { @Test void testGreeting() { MockEndpoint mockEndpoint = getMockEndpoint("mock:result"); mockEndpoint.expectedBodiesReceived("Hello World"); template.sendBody("direct:start", "World"); mockEndpoint.assertIsSatisified(); } } 1.4.5.5.2. Using AdviceWith @QuarkusTest class SimpleTest extends CamelQuarkusTestSupport { @BeforeEach public void beforeEach() throws Exception { 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 USD{body}") .to("mock:result"); } }; } @Test void testAdvisedRoute() throws Exception { MockEndpoint mockEndpoint = getMockEndpoint("mock:result"); mockEndpoint.expectedBodiesReceived("Hello World"); template.sendBody("direct:replaced", "World"); mockEndpoint.assertIsSatisfied(); } } 1.4.5.5.3. Explicitly enabling advice When explicitly enabling advice you must invoke startRouteDefinitions when completing your AdviceWith setup. Note Invoking startRouteDefinitions is only required if you have routes configured that are NOT being advised. 1.4.5.6. Limitations 1.4.5.6.1. 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 1.4.5.6.2. 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 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 @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.

Commands:
cd <directory_name>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>com.redhat.quarkus.platform</quarkus.platform.group-id> <quarkus.platform.version> <!-- The latest 3.15.x version from https://maven.repository.redhat.com/ga/com/redhat/quarkus/platform/quarkus-bom --> </quarkus.platform.version> <dependency> <groupId>USD{quarkus.platform.group-id}</groupId> <artifactId>USD{quarkus.platform.artifact-id}</artifactId> <version>USD{quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>USD{quarkus.platform.group-id}</groupId> <artifactId>quarkus-camel-bom</artifactId> <version>USD{quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency>
package org.acme.rest.json; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.model.rest.RestBindingMode; /** * Camel route definitions. */ public class Routes extends RouteBuilder { private final Set<Fruit> fruits = Collections.synchronizedSet(new LinkedHashSet<>()); private final Set<Legume> legumes = Collections.synchronizedSet(new LinkedHashSet<>()); public Routes() { /* Let's add some initial fruits */ this.fruits.add(new Fruit("Apple", "Winter fruit")); this.fruits.add(new Fruit("Pineapple", "Tropical fruit")); /* Let's add some initial legumes */ this.legumes.add(new Legume("Carrot", "Root vegetable, usually orange")); this.legumes.add(new Legume("Zucchini", "Summer squash")); } @Override public void configure() throws Exception { restConfiguration().bindingMode(RestBindingMode.json); rest("/fruits") .get() .to("direct:getFruits") .post() .type(Fruit.class) .to("direct:addFruit"); rest("/legumes") .get() .to("direct:getLegumes"); from("direct:getFruits") .setBody().constant(fruits); from("direct:addFruit") .process().body(Fruit.class, fruits::add) .setBody().constant(fruits); from("direct:getLegumes") .setBody().constant(legumes); } }
mvn clean compile quarkus:dev
package org.acme; import io.quarkus.test.junit.QuarkusTest; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; import org.hamcrest.Matchers; @QuarkusTest public class RoutesTest { @Test public void testFruitsEndpoint() { /* Assert the initial fruit is there */ given() .when().get("/fruits") .then() .statusCode(200) .body( "USD.size()", Matchers.is(1), "name", Matchers.contains("Orange")); /* Add a new fruit */ given() .body("{\"name\": \"Pear\"}") .header("Content-Type", "application/json") .when() .post("/fruits") .then() .statusCode(200); /* Assert that pear was added */ given() .when().get("/fruits") .then() .statusCode(200) .body( "USD.size()", Matchers.is(2), "name", Matchers.contains("Orange", "Pear")); } }
mvn clean test
package org.acme; import io.quarkus.test.junit.NativeImageTest; @NativeImageTest public class NativeRoutesIT extends RoutesTest { // Execute the same tests but in native mode. }
mvn clean verify -Pnative
mvn clean package ls -lh target/quarkus-app -rw-r--r--. 1 user user 238K Oct 11 18:55 quarkus-run.jar
java -jar target/quarkus-app/quarkus-run.jar [io.quarkus] (main) Quarkus started in 1.163s. Listening on: http://[::]:8080
mvn clean package -Pnative ls -lh target -rwxr-xr-x. 1 user user 46M Oct 11 18:57 code-with-quarkus-1.0.0-SNAPSHOT-runner
./target/*-runner [io.quarkus] (main) Quarkus started in 0.013s. Listening on: http://[::]:8080
ps -o rss,command -p USD(pgrep code-with) RSS COMMAND 65852 ./target/code-with-quarkus-1.0.0-SNAPSHOT-runner
import io.quarkus.test.junit.QuarkusTest; import org.junit.jupiter.api.Test; @QuarkusTest class MyTest { @Test public void test() { // Use any suitable code that sends test data to the route and then assert outcomes } }
import io.quarkus.test.junit.QuarkusIntegrationTest; @QuarkusIntegrationTest class MyIT extends MyTest { }
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(); } }
import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; @QuarkusTest @QuarkusTestResource(MyTestResource.class) class MyTest { }
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. 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(); } } }
import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; @QuarkusTest @QuarkusTestResource(WireMockTestResource.class) class MyTest { }
<dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-junit5</artifactId> <scope>test</scope> </dependency>
@QuarkusTest @TestProfile(SimpleTest.class) //necessary only if "newly created" context is required for the test (worse performance) public class SimpleTest extends CamelQuarkusTestSupport { }
@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; } }
@QuarkusTest class SimpleTest extends CamelQuarkusTestSupport { @Test void testGreeting() { MockEndpoint mockEndpoint = getMockEndpoint("mock:result"); mockEndpoint.expectedBodiesReceived("Hello World"); template.sendBody("direct:start", "World"); mockEndpoint.assertIsSatisified(); } @Override protected RoutesBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start") .transform().simple("Hello USD{body}") .to("mock:result"); } }; } }
@QuarkusTest class MyTestProfile implements QuarkusTestProfile { }
// @TestProfile will trigger the application to be restarted @TestProfile(MyTestProfile.class) @QuarkusTest class SimpleTest extends CamelQuarkusTestSupport { }
public class MyRoutes extends RouteBuilder { @Override public void configure() { from("direct:start") .transform().simple("Hello USD{body}") .to("mock:result"); } }
@QuarkusTest class SimpleTest extends CamelQuarkusTestSupport { @Test void testGreeting() { MockEndpoint mockEndpoint = getMockEndpoint("mock:result"); mockEndpoint.expectedBodiesReceived("Hello World"); template.sendBody("direct:start", "World"); mockEndpoint.assertIsSatisified(); } }
@QuarkusTest class SimpleTest extends CamelQuarkusTestSupport { @BeforeEach public void beforeEach() throws Exception { 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 USD{body}") .to("mock:result"); } }; } @Test void testAdvisedRoute() throws Exception { MockEndpoint mockEndpoint = getMockEndpoint("mock:result"); mockEndpoint.expectedBodiesReceived("Hello World"); template.sendBody("direct:replaced", "World"); mockEndpoint.assertIsSatisfied(); } }
public class MyBeanProducers { @Produces @Named("myBean") public MyBean createMyBean() { return new MyBean(); } }
@QuarkusTest class SimpleTest extends CamelQuarkusTestSupport { @BindToRegistry("myBean") MyBean myBean = new MyBean(); }