Add Unit Testing examples
diff --git a/README.adoc b/README.adoc
index d169916..cb49d8a 100644
--- a/README.adoc
+++ b/README.adoc
@@ -27,7 +27,7 @@
== Examples
// examples: START
-Number of Examples: 46 (0 deprecated)
+Number of Examples: 47 (0 deprecated)
[width="100%",cols="4,2,4",options="header"]
|===
@@ -53,6 +53,8 @@
| link:camel-example-spring-boot-type-converter/README.adoc[Spring Boot Type Converter] (camel-example-spring-boot-type-converter) | Beginner | An example showing how to create custom type converter with Camel and Spring Boot
+| link:camel-example-spring-boot-unit-testing/README.adoc[Spring Boot Unit Testing] (camel-example-spring-boot-unit-testing) | Beginner | An example showing how to write unit tests with Camel and Spring Boot
+
| link:camel-example-spring-boot-xml/readme.adoc[Spring Boot XML] (camel-example-spring-boot-xml) | Beginner | An example showing how to work with Camel routes in XML files and Spring Boot
| link:camel-example-spring-boot-aws2-s3/README.adoc[Spring Boot Aws2 S3] (camel-example-spring-boot-aws2-s3) | Cloud | An example showing the Camel AWS2 S3 component with Spring Boot
diff --git a/camel-example-spring-boot-unit-testing/README.adoc b/camel-example-spring-boot-unit-testing/README.adoc
new file mode 100644
index 0000000..4f7bdc6
--- /dev/null
+++ b/camel-example-spring-boot-unit-testing/README.adoc
@@ -0,0 +1,22 @@
+== Camel Example Spring Boot
+
+This example different ways to unit test Camel routes in Spring Boot using Mock component and adviceWith techniques.
+
+=== How to run
+
+You can run this example using
+
+ mvn test
+
+=== Help and contributions
+
+If you hit any problem using Camel or have some feedback, then please
+https://camel.apache.org/support.html[let us know].
+
+We also love contributors, so
+https://camel.apache.org/contributing.html[get involved] :-)
+
+The Camel riders!
+
+
+
diff --git a/camel-example-spring-boot-unit-testing/pom.xml b/camel-example-spring-boot-unit-testing/pom.xml
new file mode 100644
index 0000000..d0c1222
--- /dev/null
+++ b/camel-example-spring-boot-unit-testing/pom.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>examples</artifactId>
+ <groupId>org.apache.camel.springboot.example</groupId>
+ <version>3.7.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>camel-example-spring-boot-unit-testing</artifactId>
+ <name>Camel SB Examples :: Unit testing with Camel</name>
+ <description>An example showing how to write unit tests with Camel and Spring Boot</description>
+
+ <properties>
+ <category>Beginner</category>
+
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ <spring.boot-version>${spring-boot-version}</spring.boot-version>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <!-- Spring Boot BOM -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-dependencies</artifactId>
+ <version>${spring.boot-version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <!-- Camel BOM -->
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-spring-boot-bom</artifactId>
+ <version>${camel-version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+
+ <!-- Spring Boot -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter</artifactId>
+ </dependency>
+ <!-- Camel -->
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-spring-boot-starter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-stream-starter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-jms-starter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-activemq-starter</artifactId>
+ </dependency>
+
+ <!-- test -->
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test-spring-junit5</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ <version>${spring-boot-version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>repackage</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- Camel Maven Package Plugin to generate Type Converter Loader source code - Camel 3.x way -->
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>initialize</phase>
+ <goals>
+ <goal>add-source</goal>
+ <goal>add-resource</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>src/generated/java</source>
+ </sources>
+ <resources>
+ <resource>
+ <directory>src/generated/resources</directory>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- Adds mgenerated Type Converter Loader source in src/generated to the source path - Camel 3.x way -->
+ <plugin>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-package-maven-plugin</artifactId>
+ <version>${camel-version}</version>
+ <executions>
+ <execution>
+ <id>generate</id>
+ <goals>
+ <goal>generate-component</goal>
+ </goals>
+ <phase>process-classes</phase>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/camel-example-spring-boot-unit-testing/src/main/java/org/apache/camel/example/spring/boot/Application.java b/camel-example-spring-boot-unit-testing/src/main/java/org/apache/camel/example/spring/boot/Application.java
new file mode 100644
index 0000000..7a00387
--- /dev/null
+++ b/camel-example-spring-boot-unit-testing/src/main/java/org/apache/camel/example/spring/boot/Application.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * * Licensed to the Apache Software Foundation (ASF) under one or more
+ * * contributor license agreements. See the NOTICE file distributed with
+ * * this work for additional information regarding copyright ownership.
+ * * The ASF licenses this file to You under the Apache License, Version 2.0
+ * * (the "License"); you may not use this file except in compliance with
+ * * the License. You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ *
+ */
+package org.apache.camel.example.spring.boot;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class);
+ }
+}
diff --git a/camel-example-spring-boot-unit-testing/src/main/java/org/apache/camel/example/spring/boot/InterceptorSimulatedErrorException.java b/camel-example-spring-boot-unit-testing/src/main/java/org/apache/camel/example/spring/boot/InterceptorSimulatedErrorException.java
new file mode 100644
index 0000000..eb8d38a
--- /dev/null
+++ b/camel-example-spring-boot-unit-testing/src/main/java/org/apache/camel/example/spring/boot/InterceptorSimulatedErrorException.java
@@ -0,0 +1,29 @@
+/*
+ *
+ * * 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.example.spring.boot;
+
+public class InterceptorSimulatedErrorException extends RuntimeException{
+
+ public InterceptorSimulatedErrorException() {
+ }
+
+ public InterceptorSimulatedErrorException(String message) {
+ super(message);
+ }
+}
diff --git a/camel-example-spring-boot-unit-testing/src/main/java/org/apache/camel/example/spring/boot/MockSimulatedErrorException.java b/camel-example-spring-boot-unit-testing/src/main/java/org/apache/camel/example/spring/boot/MockSimulatedErrorException.java
new file mode 100644
index 0000000..4f5863f
--- /dev/null
+++ b/camel-example-spring-boot-unit-testing/src/main/java/org/apache/camel/example/spring/boot/MockSimulatedErrorException.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * * Licensed to the Apache Software Foundation (ASF) under one or more
+ * * contributor license agreements. See the NOTICE file distributed with
+ * * this work for additional information regarding copyright ownership.
+ * * The ASF licenses this file to You under the Apache License, Version 2.0
+ * * (the "License"); you may not use this file except in compliance with
+ * * the License. You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ *
+ */
+
+package org.apache.camel.example.spring.boot;
+
+public class MockSimulatedErrorException extends RuntimeException{
+
+ public MockSimulatedErrorException() {
+ }
+
+ public MockSimulatedErrorException(String message) {
+ super(message);
+ }
+}
diff --git a/camel-example-spring-boot-unit-testing/src/main/java/org/apache/camel/example/spring/boot/UnitTestsRouter.java b/camel-example-spring-boot-unit-testing/src/main/java/org/apache/camel/example/spring/boot/UnitTestsRouter.java
new file mode 100644
index 0000000..b264332
--- /dev/null
+++ b/camel-example-spring-boot-unit-testing/src/main/java/org/apache/camel/example/spring/boot/UnitTestsRouter.java
@@ -0,0 +1,125 @@
+/*
+ *
+ * * Licensed to the Apache Software Foundation (ASF) under one or more
+ * * contributor license agreements. See the NOTICE file distributed with
+ * * this work for additional information regarding copyright ownership.
+ * * The ASF licenses this file to You under the Apache License, Version 2.0
+ * * (the "License"); you may not use this file except in compliance with
+ * * the License. You may obtain a copy of the License at
+ * *
+ * * http://www.apache.org/licenses/LICENSE-2.0
+ * *
+ * * Unless required by applicable law or agreed to in writing, software
+ * * distributed under the License is distributed on an "AS IS" BASIS,
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * * See the License for the specific language governing permissions and
+ * * limitations under the License.
+ *
+ */
+package org.apache.camel.example.spring.boot;
+
+import java.util.ArrayList;
+
+import org.apache.camel.builder.RouteBuilder;
+
+import org.springframework.stereotype.Component;
+
+import static org.apache.camel.builder.AggregationStrategies.flexible;
+
+@Component
+public class UnitTestsRouter extends RouteBuilder {
+ @Override
+ public void configure() throws Exception {
+
+ // @formatter:off
+
+ // Example 5 : Simulating error from mocked endpoint
+ onException(MockSimulatedErrorException.class)
+ .handled(true)
+ .to("mock:g");
+
+ // Example 6 : Simulating error from mocked endpoint
+ onException(InterceptorSimulatedErrorException.class)
+ .handled(true)
+ .to("mock:j");
+
+ // Example 1 : Simple mock testing
+ from("direct:simple-mock")
+ .to("mock:a");
+
+ // Example 2 : Mock testing with expects expression
+ from("direct:mock-expects-expression")
+ .to("mock:b");
+
+ // Example 3 : Mock testing with descending counter expression
+ from("direct:mock-expects-descending")
+ .to("mock:c");
+
+ // Example 3 : Mock testing with ascending counter expression
+ from("direct:mock-expects-ascending")
+ .to("mock:d");
+
+ // Example 4 : Providing canned response from mocked endpoint
+ from("direct:mock-response")
+ .to("mock:e");
+
+ // Example 5 : Simulating error from mocked endpoint
+ from("direct:mock-simulate-error")
+ .to("mock:f");
+
+ // Example 6 : Simulating error with interceptor
+ from("direct:interceptor-simulate-error").routeId("example6")
+ .to("mock:h");
+
+ // Example 7 : Advising and mocking endpoints
+ from("direct:advice-mock-endpoints").routeId("example7")
+ .choice()
+ .when(simple("${body} contains 'K'"))
+ .to("seda:k")
+ .otherwise()
+ .to("seda:l");
+
+ // Example 8 : Advising and mocking endpoints
+ from("jms:abc").routeId("example8")
+ .choice()
+ .when(simple("${body} contains 'M'"))
+ .to("seda:m")
+ .otherwise()
+ .to("seda:n");
+
+ // Example 9 : Weaving processor by ID
+ from("direct:advice-weave-by-id-replace").routeId("example9")
+ .transform(simple("${body.toLowerCase()}")).id("node9");
+
+ // Example 10 : Weaving producer by ID and replacing node
+ from("direct:advice-producer-by-id-replace").routeId("example10")
+ .split(body(), flexible().accumulateInCollection(ArrayList.class))
+ .transform(simple("${body.toLowerCase()}"))
+ .to("seda:p")
+ .end()
+ .to("mock:q");
+
+ // Example 11 : Weaving producer by ID and removing node
+ from("direct:advice-weave-by-id-remove").routeId("example11")
+ .transform(simple("${body.toLowerCase()}")).id("node11");
+
+ // Example 12 : Weaving node by type
+ from("direct:advice-weave-by-type").routeId("example12")
+ .split(body(), flexible().accumulateInCollection(ArrayList.class))
+ .transform(simple("${body.toLowerCase()}"))
+ .to("mock:t")
+ .end()
+ .to("mock:u");
+
+ // Example 13 : Selecting specific node using select* method
+ from("direct:advice-weave-by-type-and-select").routeId("example13")
+ .split(body(), flexible().accumulateInCollection(ArrayList.class))
+ .transform(simple("${body.toUpperCase()}"))
+ .to("mock:v")
+ .end()
+ .to("mock:w");
+ // @formatter:on
+
+ }
+
+}
diff --git a/camel-example-spring-boot-unit-testing/src/main/resources/application.properties b/camel-example-spring-boot-unit-testing/src/main/resources/application.properties
new file mode 100644
index 0000000..4ec0ff7
--- /dev/null
+++ b/camel-example-spring-boot-unit-testing/src/main/resources/application.properties
@@ -0,0 +1,10 @@
+# the name of Camel
+camel.springboot.name = CamelUnitTestsExample
+
+# to configure logging levels
+logging.level.org.springframework = INFO
+logging.level.org.apache.camel.spring.boot = INFO
+logging.level.org.apache.camel.impl = INFO
+logging.level.org.apache.camel = INFO
+logging.level.sample.camel = INFO
+#logging.level.sample.camel = DEBUG
diff --git a/camel-example-spring-boot-unit-testing/src/test/java/CamelSpringBootUnitTests.java b/camel-example-spring-boot-unit-testing/src/test/java/CamelSpringBootUnitTests.java
new file mode 100644
index 0000000..dcad167
--- /dev/null
+++ b/camel-example-spring-boot-unit-testing/src/test/java/CamelSpringBootUnitTests.java
@@ -0,0 +1,425 @@
+/*
+ *
+ * * 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.
+ *
+ */
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.AdviceWith;
+import org.apache.camel.builder.AdviceWithRouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.example.spring.boot.Application;
+import org.apache.camel.example.spring.boot.InterceptorSimulatedErrorException;
+import org.apache.camel.example.spring.boot.MockSimulatedErrorException;
+import org.apache.camel.model.SplitDefinition;
+import org.apache.camel.model.ToDefinition;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import static org.apache.camel.builder.Builder.body;
+import static org.apache.camel.builder.Builder.simple;
+import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+@CamelSpringBootTest
+@SpringBootTest(classes = Application.class)
+public class CamelSpringBootUnitTests {
+
+ @Autowired
+ private CamelContext camelContext;
+
+ @Autowired
+ private ProducerTemplate producerTemplate;
+
+ // @formatter:off
+ @Test
+ public void testSimpleMock() throws InterruptedException {
+ // Getting mock endpoint object from the context
+ MockEndpoint mockA = camelContext.getEndpoint("mock:a", MockEndpoint.class);
+
+ // setting expectation for a message body received by mock using one the expect* methods
+ mockA.expectedBodiesReceived("Hello world");
+ // setting expectation for a message body received by mock using one the expect* methods
+ mockA.message(0).header("Counter").isEqualTo(1);
+
+ // sending data to route consumer
+ producerTemplate.sendBodyAndHeader("direct:simple-mock", "Hello world", "Counter", 1);
+
+ // asserting expectations are satisfied
+ mockA.assertIsSatisfied();
+ }
+
+ @Test
+ public void testMockWithCustomExpect() throws Exception {
+ // Getting mock endpoint object from the context
+ MockEndpoint mockB = camelContext.getEndpoint("mock:b", MockEndpoint.class);
+
+ // setting expectation for a number of messages that will arrive at mock
+ mockB.expectedMessageCount(3);
+
+ // expects method allow providing custom logic to verify expectations
+ mockB.expects(() -> mockB.getExchanges().stream()
+ .reduce(0, (previous, exchange) -> {
+ int current = exchange.getIn().getHeader("Counter", Integer.class);
+ if (current <= previous){
+ // We fail explicitly if counter current counter is less than previous
+ fail ("Counter is not greater than previous counter");
+ } else if (current - previous != 1){
+ // We fail explicitly if gap more than 1 detected between previous and current counter
+ fail("Gap detected: previous: " + previous + " current: " + current);
+ }
+ return current;
+ // Combiner
+ }, (prev, current) -> current));
+
+ // Change to non consequent counter number to see test fail
+ // Sending test data to route consumer
+ producerTemplate.sendBodyAndHeader("direct:mock-expects-expression", "A", "Counter", 1);
+ producerTemplate.sendBodyAndHeader("direct:mock-expects-expression", "A", "Counter", 2);
+ producerTemplate.sendBodyAndHeader("direct:mock-expects-expression", "A", "Counter", 3);
+
+ // Asserting expectations are satisfied
+ mockB.assertIsSatisfied();
+ }
+
+ @Test
+ public void testMockExpectDescending() throws Exception {
+ // Getting mock endpoint object from the context
+ MockEndpoint mockC = camelContext.getEndpoint("mock:c", MockEndpoint.class);
+
+ // setting expectation for a number of messages that will arrive at mock
+ mockC.expectedMessageCount(3);
+
+ // setting expectation for a attribute, in this case defined in header to be in a descending order
+ mockC.expectsDescending().header("Counter");
+ // setting expectation for a message body
+ mockC.allMessages().body().isEqualTo("A");
+
+ // Change to non consequent counter number to see test fail
+ // Sending test data to route consumer
+ // TODO 7 : Does not work exactly descending 1, 2, 1
+ producerTemplate.sendBodyAndHeader("direct:mock-expects-descending", "A", "Counter", 3);
+ producerTemplate.sendBodyAndHeader("direct:mock-expects-descending", "A", "Counter", 2);
+ producerTemplate.sendBodyAndHeader("direct:mock-expects-descending", "A", "Counter", 1);
+
+ // Asserting expectations are satisfied
+ mockC.assertIsSatisfied();
+ }
+
+ @Test
+ public void testMockExpectAscending() throws Exception {
+ // Getting mock endpoint object from the context
+ MockEndpoint mockD = camelContext.getEndpoint("mock:d", MockEndpoint.class);
+
+ // setting expectation for a number of messages that will arrive at mock
+ mockD.expectedMessageCount(3);
+
+ // setting expectation for a attribute, in this case defined in header to be in a ascending order
+ mockD.expectsAscending().header("Counter");
+ // setting expectation for a message body to contain no duplicates
+ mockD.expectsNoDuplicates().body();
+
+ // Change to non consequent counter number to see test fail
+ // Sending test data to route consumer
+ producerTemplate.sendBodyAndHeader("direct:mock-expects-ascending", "A", "Counter", 1);
+ producerTemplate.sendBodyAndHeader("direct:mock-expects-ascending", "B", "Counter", 2);
+ producerTemplate.sendBodyAndHeader("direct:mock-expects-ascending", "C", "Counter", 3);
+
+ // asserting expectations are satisfied
+ mockD.assertIsSatisfied();
+ }
+
+ @Test
+ public void testMockComponent() throws Exception {
+ // Getting mock endpoint object from the context
+ MockEndpoint mockE = camelContext.getEndpoint("mock:e", MockEndpoint.class);
+
+ // setting expectation for a number of messages that will arrive at mock
+ mockE.expectedMessageCount(2);
+ // setting expectation for the contents of messages that will arrive at mock
+ mockE.expectedBodiesReceived("1", "2");
+
+ // defining behavior of a mock by providing a canned stubbed response to any exchanges arrived
+ mockE.whenAnyExchangeReceived(e -> e.getIn().setBody("B"));
+ // defining behavior of a mock by providing a canned stubbed response for a particular exchange by index
+ mockE.whenExchangeReceived(2, e -> e.getIn().setBody("C"));
+
+ // Change to non consequent counter number to see test fail
+ // Sending test data to route consumer and obtaining response messages for further validation
+ final String mockResponse1 = producerTemplate.requestBody("direct:mock-response", "1", String.class);
+ final String mockResponse2 = producerTemplate.requestBody("direct:mock-response", "2", String.class);
+
+ // Asserting expectations are satisfied
+ mockE.assertIsSatisfied();
+ // Asserting expectations towards message body contents are satisfied
+ assertEquals("B", mockResponse1);
+ assertEquals("C", mockResponse2);
+ }
+
+ @Test
+ public void testMockSimulateError() throws Exception {
+ // Getting mock endpoint object from the context
+ MockEndpoint mockF = camelContext.getEndpoint("mock:f", MockEndpoint.class);
+ MockEndpoint mockG = camelContext.getEndpoint("mock:g", MockEndpoint.class);
+
+ // defining behavior of a mock by providing a canned stubbed response (exception) to any exchanges arrived
+ mockF.whenAnyExchangeReceived(e -> e.setException(new MockSimulatedErrorException("Simulated error")));
+
+ // setting expectation for the contents of messages that will arrive at mock
+ mockG.expectedBodiesReceived("A");
+
+ // Sending test data to route consumer
+ producerTemplate.sendBody("direct:mock-simulate-error", "A");
+
+ // Asserting expectations are satisfied
+ mockF.assertIsSatisfied();
+ mockG.assertIsSatisfied();
+ }
+
+ @Test
+ public void testMockSimulateErrorWithInterceptor() throws Exception {
+ // Getting mock endpoint object from the context
+ MockEndpoint mockH = camelContext.getEndpoint("mock:h", MockEndpoint.class);
+ MockEndpoint mockJ = camelContext.getEndpoint("mock:j", MockEndpoint.class);
+
+ // Advising endpoint in AOP style by providing routeId
+ AdviceWith.adviceWith(camelContext, "example6",
+ // intercepting an exchange on route to the endpoint
+ a -> a.interceptSendToEndpoint("mock:h")
+ // skipping sending exchange to the original endpoint
+ .skipSendToOriginalEndpoint()
+ // Throwing an error to simulate failure
+ .process(exchange -> {
+ throw new InterceptorSimulatedErrorException("Error");
+ }));
+
+ // setting expectation for the absence of messages that will arrive at mock
+ mockH.expectedMessageCount(0);
+ // setting expectation for the contents of messages that will arrive at mock
+ mockJ.expectedBodiesReceived("A");
+
+ // Sending test data to route consumer
+ producerTemplate.sendBody("direct:interceptor-simulate-error", "A");
+
+ // Asserting expectations are satisfied
+ mockH.assertIsSatisfied();
+ mockJ.assertIsSatisfied();
+ }
+
+ @Test
+ public void testAdvisingRouteAndMockingEndpoints() throws Exception {
+ // Advising endpoint in AOP style by providing routeId
+ AdviceWith.adviceWith(camelContext, "example7",
+ // mocking all endpoint in advised route
+ AdviceWithRouteBuilder::mockEndpoints);
+
+ // Getting mock endpoint object from the context
+ MockEndpoint mockK = camelContext.getEndpoint("mock:seda:k", MockEndpoint.class);
+ MockEndpoint mockL = camelContext.getEndpoint("mock:seda:l", MockEndpoint.class);
+
+ // setting expectation for the contents of messages that will arrive at mock
+ mockK.expectedBodiesReceived("K");
+ mockL.expectedBodiesReceived("L");
+
+ // Sending test data to route consumer
+ producerTemplate.sendBody("direct:advice-mock-endpoints", "K");
+ producerTemplate.sendBody("direct:advice-mock-endpoints", "L");
+
+ // Asserting expectations are satisfied
+ mockK.assertIsSatisfied();
+ mockL.assertIsSatisfied();
+ }
+
+
+ @Test
+ public void testAdvisedRouteAndWeaveConsumerFromWith() throws Exception {
+ // Getting mock endpoint object from the context
+ MockEndpoint mockM = camelContext.getEndpoint("mock:seda:m", MockEndpoint.class);
+ MockEndpoint mockN = camelContext.getEndpoint("mock:seda:n", MockEndpoint.class);
+
+ // Advising endpoint in AOP style by providing routeId
+ AdviceWith.adviceWith(camelContext, "example8",
+ a -> {
+ // Replacing a consumer (from) with a another consumer
+ a.replaceFromWith("direct:advice-replace-consumer");
+ // Mocking particular endpoint by providing a pattern match
+ a.mockEndpoints("seda:*");
+ });
+
+ // setting expectation for the contents of messages that will arrive at mock
+ mockM.expectedBodiesReceived("M");
+ mockN.expectedBodiesReceived("N");
+
+ // Sending test data to route consumer
+ producerTemplate.sendBody("direct:advice-replace-consumer", "M");
+ producerTemplate.sendBody("direct:advice-replace-consumer", "N");
+
+ // Asserting expectations are satisfied
+ mockM.assertIsSatisfied();
+ mockN.assertIsSatisfied();
+ }
+
+ @Test
+ public void testAdvisingRouteAndWeaveByIdAndReplacingNode() throws Exception {
+ // Advising endpoint in AOP style by providing routeId
+ AdviceWith.adviceWith(camelContext, "example9",
+ a -> { // weaving particular node in the route by id
+ a.weaveById("node9")
+ // providing advised (weaved) node replacement
+ .replace().transform(simple("${body.toUpperCase()}"));
+ // adding new (mock) node to the route definition
+ a.weaveAddLast().to("mock:o"); });
+
+ // Getting mock endpoint object from the context
+ MockEndpoint mockO = camelContext.getEndpoint("mock:o", MockEndpoint.class);
+
+
+
+ // setting expectation for the contents of messages that will arrive at mock
+ mockO.expectedBodiesReceived("HELLO WORLD");
+
+ // Sending test data to route consumer
+ producerTemplate.sendBody("direct:advice-weave-by-id-replace", "Hello World");
+
+ // Asserting expectations are satisfied
+ mockO.assertIsSatisfied();
+ }
+
+
+ @Test
+ public void testAdvisingRouteAndProducerWeaveByToUriAndReplaceNode() throws Exception {
+ // Getting mock endpoint object from the context
+ MockEndpoint mockP = camelContext.getEndpoint("mock:p", MockEndpoint.class);
+ MockEndpoint mockQ = camelContext.getEndpoint("mock:q", MockEndpoint.class);
+
+ // Advising endpoint in AOP style by providing routeId
+ AdviceWith.adviceWith(camelContext, "example10",
+ a -> { // weaving particular node in the route by URI
+ a.weaveByToUri("seda:p")
+ // providing advised (weaved) node replacement
+ .replace().to("mock:p"); });
+
+ // setting expectation for the contents of messages that will arrive at mock
+ mockP.expectedBodiesReceived("a", "b", "c");
+ mockQ.message(0).body().isInstanceOf(List.class);
+
+ // Sending test data to route consumer
+ producerTemplate.sendBody("direct:advice-producer-by-id-replace", "a,b,c");
+
+ // Asserting expectations are satisfied
+ mockQ.assertIsSatisfied();
+ mockP.assertIsSatisfied();
+ }
+
+ @Test
+ public void testAdvisingRouteAndWeaveByIdAndRemoveNode() throws Exception {
+ // Advising endpoint in AOP style by providing routeId
+ AdviceWith.adviceWith(camelContext, "example11",
+ a -> { // adding a node at the end of the route definition
+ a.weaveAddLast().to("mock:r");
+ // weaving particular node in the route by URI
+ a.weaveById("node11")
+ // and removing advised node
+ .remove();
+ });
+
+ // Getting mock endpoint object from the context
+ MockEndpoint mockR = camelContext.getEndpoint("mock:r", MockEndpoint.class);
+
+ // setting expectation for the contents of messages that will arrive at mock
+ mockR.expectedBodiesReceived("Hello World");
+
+ // Sending test data to route consumer
+ producerTemplate.sendBody("direct:advice-weave-by-id-remove", "Hello World");
+
+ // Asserting expectations are satisfied
+ mockR.assertIsSatisfied();
+ }
+
+ @Test
+ public void testAdvisingRouteAndWeaveByIdAndAddNode() throws Exception {
+ // Advising endpoint in AOP style by providing routeId
+ AdviceWith.adviceWith(camelContext, "example12",
+ // find the splitter node
+ a -> a.weaveByType(SplitDefinition.class)
+ // and insert the route snippet before it
+ .before()
+ // definition of the newly added node
+ .filter(body().contains("c"))
+ .transform(simple("${body} advised")));
+
+ // Getting mock endpoint object from the context
+ MockEndpoint mockT = camelContext.getEndpoint("mock:t", MockEndpoint.class);
+ MockEndpoint mockU = camelContext.getEndpoint("mock:u", MockEndpoint.class);
+
+ // setting expectation for the contents of messages that will arrive at mock
+ mockT.expectedBodiesReceived("a", "b", "c advised");
+ mockU.message(0).body().isInstanceOf(List.class);
+
+ // Sending test data to route consumer
+ producerTemplate.sendBody("direct:advice-weave-by-type", "a,b,c");
+
+ // Asserting expectations are satisfied
+ mockT.assertIsSatisfied();
+ mockU.assertIsSatisfied();
+
+ // Resetting mock endpoints and removing weaved new node
+ MockEndpoint.resetMocks(camelContext);
+
+ // Sending test data to route consumer - this time without
+ producerTemplate.sendBody("direct:advice-weave-by-type", "a,b,c");
+ mockT.expectedBodiesReceived("a", "b", "c");
+ mockU.message(0).body().isInstanceOf(List.class);
+
+ // Asserting expectations are satisfied
+ mockT.assertIsSatisfied();
+ mockU.assertIsSatisfied();
+ }
+
+ @Test
+ public void testAdvisingRouteAndWeaveByTypeAndReplacingNodeWithSelect() throws Exception {
+ // Advising endpoint in AOP style by providing routeId
+ AdviceWith.adviceWith(camelContext, "example13",
+ // find by type the 'to' (consumer)
+ a -> a.weaveByType(ToDefinition.class)
+ // select first matching type using on of select* methods
+ .selectFirst()
+ // and replace it with the another consumer
+ .replace().to("mock:x"));
+
+ // Getting mock endpoint object from the context
+ MockEndpoint mockV = camelContext.getEndpoint("mock:v", MockEndpoint.class);
+ MockEndpoint mockX = camelContext.getEndpoint("mock:x", MockEndpoint.class);
+
+ // we expect zero messages for 'mock:v' as it is weaved and replaced by 'mock:x'
+ mockV.expectedMessageCount(0);
+ // 'mock:x' will receive all messages destined to 'mock:v'
+ mockX.expectedBodiesReceived("A", "B", "C");
+
+ // Sending test data to route consumer - this time without
+ producerTemplate.sendBody("direct:advice-weave-by-type-and-select", "a,b,c");
+
+ // Asserting expectations are satisfied
+ mockV.assertIsSatisfied();
+ mockX.assertIsSatisfied();
+ }
+ // @formatter:on
+}
diff --git a/pom.xml b/pom.xml
index 67328a4..d34b302 100644
--- a/pom.xml
+++ b/pom.xml
@@ -80,7 +80,8 @@
<module>camel-example-spring-cloud-servicecall</module>
<module>camel-example-spring-cloud-serviceregistry</module>
<module>camel-example-spring-boot-widget-gadget</module>
- </modules>
+ <module>camel-example-spring-boot-unit-testing</module>
+ </modules>
<properties>
<!-- for symbolicName in OSGi examples we only want the artifactId, eg camel-example-sql -->