Mock — provides a declarative framework for testing routes
Mock endpoints provide a powerful declarative testing mechanism similar to jMock. It allows declarative expectations to be created on any Mock endpoint before a test begins. When the test is run, which typically fires messages to one or more endpoints, the expectations can be asserted in a test case to ensure the system worked as expected.
This allows you to test things like:
The correct number of messages are received on each endpoint
The correct payloads are received and in the right order
Messages arrive on an endpoint in the right order
Messages arrive on an endpoint in order according to some Expression
that
implements an order testing function
Arriving messages match some kind of predicate (such as specific headers that have
specific values) or message parts match some predicate (such as evaluation by an
XPath
or XQuery
expression)
![]() | Note |
---|---|
The Test endpoint is a mock endpoint that uses a second endpoint to provide the list of expected message bodies and automatically sets up the mock endpoint assertions. |
![]() | Important |
---|---|
Mock endpoints keep received Exchanges in memory indefinitely. When you add Mock endpoints to
a route, each Exchange sent to those endpoints is stored in memory (for later
validation) until an explicit reset or the JVM is restarted. Sending large messages
or a high volume of messages may result in excessive memory use. If your goal is to
test deployable routes inline, use Apache Camel 2.10 onwards:
Alternatively, you can use the |
Mock endpoints have the following URI format:
mock:someName
[?options
]
Where someName
is any string that uniquely
identifies the endpoint.
You can append query options to the URI in the following format,
?option=value&option=value&...
Table 51, “Mock options” lists the options for a mock endpoint.
Table 51. Mock options
Option | Default | Description |
---|---|---|
retainFirst | Specifies the number of first Exchanges to retain in memory. | |
retainLast | Specifies the number of last Exchanges to retain in memory. | |
reportGroup | null | Specifies a size to use for a throughput logger for reporting. |
Here's a simple example of Mock endpoint in use. First, the endpoint is resolved on the context. Then we set an expectation, and then, after the test has run, we assert that our expectations have been met.
MockEndpoint resultEndpoint = context.resolveEndpoint("mock:foo", MockEndpoint.class); resultEndpoint.expectedMessageCount(2); // send some messages ... // now lets assert that the mock:foo endpoint received 2 messages resultEndpoint.assertIsSatisfied();
You typically always call the assertIsSatisfied() method to test that the expectations were met after running a test.
Apache Camel will by default wait 10 seconds when the
assertIsSatisfied()
is invoked. This can be
configured by setting the
setResultWaitTime(millis)
method.
Available as of Camel 2.7 When
the assertion is satisfied then Camel will stop waiting and continue
from the assertIsSatisfied
method. That means if
a new message arrives on the mock endpoint, just a bit later, that
arrival will not affect the outcome of the assertion. Suppose you do
want to test that no new messages arrives after a period thereafter,
then you can do that by setting the
setAssertPeriod
method, for example:
MockEndpoint resultEndpoint = context.resolveEndpoint("mock:foo", MockEndpoint.class); resultEndpoint.setAssertPeriod(5000); resultEndpoint.expectedMessageCount(2); // send some messages ... // now lets assert that the mock:foo endpoint received 2 messages resultEndpoint.assertIsSatisfied();
You can see from the javadoc of MockEndpoint the various helper methods you can use to set expectations. The main methods are as follows:
Method | Description |
---|---|
expectedMessageCount(int) | To define the expected message count on the endpoint. |
expectedMinimumMessageCount(int) | To define the minimum number of expected messages on the endpoint. |
expectedBodiesReceived(...) | To define the expected bodies that should be received (in order). |
expectedHeaderReceived(...) | To define the expected header that should be received |
expectsAscending(Expression) | To add an expectation that messages are received in order, using the given Expression to compare messages. |
expectsDescending(Expression) | To add an expectation that messages are received in order, using the given Expression to compare messages. |
expectsNoDuplicates(Expression) | To add an expectation that no duplicate messages are
received; using an Expression to calculate a unique identifier for
each message. This could be something like the
JMSMessageID if using JMS, or some
unique reference number within the message. |
Here's another example:
resultEndpoint.expectedBodiesReceived("firstMessageBody", "secondMessageBody", "thirdMessageBody");
In addition, you can use the message(int messageIndex) method to add assertions about a specific message that is received.
For example, to add expectations of the headers or body of the
first message (using zero-based indexing like
java.util.List
), you can use the following
code:
resultEndpoint.message(0).header("foo").isEqualTo("bar");
There are some examples of the Mock endpoint in use in the camel-core processor tests.
Available as of Camel 2.7
Camel now allows you to automatic mock existing endpoints in your Camel routes.
![]() | Important |
---|---|
The endpoints are still in action, what happens is that a Mock endpoint is injected and receives the message first, it then delegate the message to the target endpoint. You can view this as a kind of intercept and delegate or endpoint listener. |
Suppose you have the given route below:
@Override protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start").to("direct:foo").to("log:foo").to("mock:result"); from("direct:foo").transform(constant("Bye World")); } }; }
You can then use the adviceWith
feature in
Camel to mock all the endpoints in a given route from your unit
test, as shown below:
public void testAdvisedMockEndpoints() throws Exception { // advice the first route using the inlined AdviceWith route builder // which has extended capabilities than the regular route builder context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() { @Override public void configure() throws Exception { // mock all endpoints mockEndpoints(); } }); getMockEndpoint("mock:direct:start").expectedBodiesReceived("Hello World"); getMockEndpoint("mock:direct:foo").expectedBodiesReceived("Hello World"); getMockEndpoint("mock:log:foo").expectedBodiesReceived("Bye World"); getMockEndpoint("mock:result").expectedBodiesReceived("Bye World"); template.sendBody("direct:start", "Hello World"); assertMockEndpointsSatisfied(); // additional test to ensure correct endpoints in registry assertNotNull(context.hasEndpoint("direct:start")); assertNotNull(context.hasEndpoint("direct:foo")); assertNotNull(context.hasEndpoint("log:foo")); assertNotNull(context.hasEndpoint("mock:result")); // all the endpoints was mocked assertNotNull(context.hasEndpoint("mock:direct:start")); assertNotNull(context.hasEndpoint("mock:direct:foo")); assertNotNull(context.hasEndpoint("mock:log:foo")); }
Notice that the mock endpoints is given the uri
mock:<endpoint>
, for example
mock:direct:foo
. Camel logs at
INFO
level the endpoints being mocked:
INFO Adviced endpoint [direct://foo] with mock endpoint [mock:direct:foo]
![]() | Important |
---|---|
Endpoints which are mocked will have their parameters stripped off. For example the endpoint "log:foo?showAll=true" will be mocked to the following endpoint "mock:log:foo". Notice the parameters has been removed. |
Its also possible to only mock certain endpoints using a pattern.
For example to mock all log
endpoints you do as
shown:
public void testAdvisedMockEndpointsWithPattern() throws Exception { // advice the first route using the inlined AdviceWith route builder // which has extended capabilities than the regular route builder context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() { @Override public void configure() throws Exception { // mock only log endpoints mockEndpoints("log*"); } }); // now we can refer to log:foo as a mock and set our expectations getMockEndpoint("mock:log:foo").expectedBodiesReceived("Bye World"); getMockEndpoint("mock:result").expectedBodiesReceived("Bye World"); template.sendBody("direct:start", "Hello World"); assertMockEndpointsSatisfied(); // additional test to ensure correct endpoints in registry assertNotNull(context.hasEndpoint("direct:start")); assertNotNull(context.hasEndpoint("direct:foo")); assertNotNull(context.hasEndpoint("log:foo")); assertNotNull(context.hasEndpoint("mock:result")); // only the log:foo endpoint was mocked assertNotNull(context.hasEndpoint("mock:log:foo")); assertNull(context.hasEndpoint("mock:direct:start")); assertNull(context.hasEndpoint("mock:direct:foo")); }
The pattern supported can be a wildcard or a regular expression. See more details about this at Intercept as its the same matching function used by Camel.
![]() | Important |
---|---|
Mind that mocking endpoints causes the messages to be copied when they arrive on the mock. That means Camel will use more memory. This may not be suitable when you send in a lot of messages. |
camel-test
componentInstead of using the adviceWith
to instruct
Camel to mock endpoints, you can easily enable this behavior when
using the camel-test
Test Kit. The same route can
be tested as follows. Notice that we return "*"
from the isMockEndpoints
method, which tells
Camel to mock all endpoints. If you only want to mock all
log
endpoints you can return
"log*"
instead.
public class IsMockEndpointsJUnit4Test extends CamelTestSupport { @Override public String isMockEndpoints() { // override this method and return the pattern for which endpoints to mock. // use * to indicate all return "*"; } @Test public void testMockAllEndpoints() throws Exception { // notice we have automatic mocked all endpoints and the name of the endpoints is "mock:uri" getMockEndpoint("mock:direct:start").expectedBodiesReceived("Hello World"); getMockEndpoint("mock:direct:foo").expectedBodiesReceived("Hello World"); getMockEndpoint("mock:log:foo").expectedBodiesReceived("Bye World"); getMockEndpoint("mock:result").expectedBodiesReceived("Bye World"); template.sendBody("direct:start", "Hello World"); assertMockEndpointsSatisfied(); // additional test to ensure correct endpoints in registry assertNotNull(context.hasEndpoint("direct:start")); assertNotNull(context.hasEndpoint("direct:foo")); assertNotNull(context.hasEndpoint("log:foo")); assertNotNull(context.hasEndpoint("mock:result")); // all the endpoints was mocked assertNotNull(context.hasEndpoint("mock:direct:start")); assertNotNull(context.hasEndpoint("mock:direct:foo")); assertNotNull(context.hasEndpoint("mock:log:foo")); } @Override protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start").to("direct:foo").to("log:foo").to("mock:result"); from("direct:foo").transform(constant("Bye World")); } }; } }
If you do not use the camel-test
component for
unit testing (as shown above) you can use a different approach when
using XML files for routes. The solution is to create a new XML file
used by the unit test and then include the intended XML file which
has the route you want to test.
Suppose we have the route in the
camel-route.xml
file:
<!-- this camel route is in the camel-route.xml file --> <camelContext xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="direct:start"/> <to uri="direct:foo"/> <to uri="log:foo"/> <to uri="mock:result"/> </route> <route> <from uri="direct:foo"/> <transform> <constant>Bye World</constant> </transform> </route> </camelContext>
Then we create a new XML file as follows, where we include the
camel-route.xml
file and define a spring bean
with the class
org.apache.camel.impl.InterceptSendToMockEndpointStrategy
which tells Camel to mock all endpoints:
<!-- the Camel route is defined in another XML file --> <import resource="camel-route.xml"/> <!-- bean which enables mocking all endpoints --> <bean id="mockAllEndpoints" class="org.apache.camel.impl.InterceptSendToMockEndpointStrategy"/>
Then in your unit test you load the new XML file
(test-camel-route.xml
) instead of
camel-route.xml
.
To only mock all log endpoints you can define the pattern in the constructor for the bean:
<bean id="mockAllEndpoints" class="org.apache.camel.impl.InterceptSendToMockEndpointStrategy"> <constructor-arg index="0" value="log*"/> </bean>
Available as of Camel 2.10
Sometimes you want to easily mock and skip sending to a certain
endpoints. So the message is detoured and send to the mock endpoint
only. From Camel 2.10 onwards you can now use the
mockEndpointsAndSkip
method using AdviceWith or the Test Kit. The example below will skip
sending to the two endpoints "direct:foo"
, and
"direct:bar"
.
public void testAdvisedMockEndpointsWithSkip() throws Exception { // advice the first route using the inlined AdviceWith route builder // which has extended capabilities than the regular route builder context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() { @Override public void configure() throws Exception { // mock sending to direct:foo and direct:bar and skip send to it mockEndpointsAndSkip("direct:foo", "direct:bar"); } }); getMockEndpoint("mock:result").expectedBodiesReceived("Hello World"); getMockEndpoint("mock:direct:foo").expectedMessageCount(1); getMockEndpoint("mock:direct:bar").expectedMessageCount(1); template.sendBody("direct:start", "Hello World"); assertMockEndpointsSatisfied(); // the message was not send to the direct:foo route and thus not sent to the seda endpoint SedaEndpoint seda = context.getEndpoint("seda:foo", SedaEndpoint.class); assertEquals(0, seda.getCurrentQueueSize()); }
The same example using the Test Kit
public class IsMockEndpointsAndSkipJUnit4Test extends CamelTestSupport { @Override public String isMockEndpointsAndSkip() { // override this method and return the pattern for which endpoints to mock, // and skip sending to the original endpoint. return "direct:foo"; } @Test public void testMockEndpointAndSkip() throws Exception { // notice we have automatic mocked the direct:foo endpoints and the name of the endpoints is "mock:uri" getMockEndpoint("mock:result").expectedBodiesReceived("Hello World"); getMockEndpoint("mock:direct:foo").expectedMessageCount(1); template.sendBody("direct:start", "Hello World"); assertMockEndpointsSatisfied(); // the message was not send to the direct:foo route and thus not sent to the seda endpoint SedaEndpoint seda = context.getEndpoint("seda:foo", SedaEndpoint.class); assertEquals(0, seda.getCurrentQueueSize()); } @Override protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { @Override public void configure() throws Exception { from("direct:start").to("direct:foo").to("mock:result"); from("direct:foo").transform(constant("Bye World")).to("seda:foo"); } }; } }
You can use the retainFirst
and retainLast
options
together to instruct Mock endpoints to retain only the specified number of first and
last Exchanges in memory. The following code snippet retains a copy of the first five
and last five Exchanges that the Mock endpoint receives:
MockEndpoint mock = getMockEndpoint("mock:data"); mock.setRetainFirst(5); mock.setRetainLast(5); mock.expectedMessageCount(2000); ... mock.assertIsSatisfied();
Some limitations are associated with using these options. The
getExchanges()
and getReceivedExchanges()
methods on the MockEndpoint
return only the retained copies of the
Exchanges. So, for our example snippet, the returned list would contain only ten
Exchanges—the first five and the last five.
The retainFirst
and retainLast
options also limit
which expectation methods you can use. For example, the
expected
XXX
methods that work on
message bodies, headers, and so on, work only on retained messages. For our example
snippet, expected
XXX
methods would test
expectations only on the ten retained Exchanges.
Available as of Camel 2.7
The Mock endpoint stores the arrival time of the message as a property on the Exchange.
Date time = exchange.getProperty(Exchange.RECEIVED_TIMESTAMP, Date.class);
You can use this information to discover when the message arrived
on the mock. But it also provides the foundation for discovering the
time interval between the previous and next message arrived on the
mock. You can use this information to set expectations using the
arrives
DSL on the Mock endpoint.
For example, to specify that the first message should arrive between 0 and 2 seconds before the next you can do:
mock.message(0).arrives().noLaterThan(2).seconds().beforeNext();
You can also specify that 2nd message (0 index based) should arrive no later than 0-2 seconds after the previous:
mock.message(1).arrives().noLaterThan(2).seconds().afterPrevious();
You can also use between
to set a lower bound.
For example, suppose that it should be between 1-4 seconds:
mock.message(1).arrives().between(1, 4).seconds().afterPrevious();
You can also set the expectation on all messages; for example, to specify that the gap between them should be at most 1 second:
mock.allMessages().arrives().noLaterThan(1).seconds().beforeNext();
![]() | Note |
---|---|
In the example above we use |