Testing Camel with MockEndpoints - java

I've got a series of "pipelined" components that all communicate through ActiveMQ message queues. Each component uses Camel to treat each of these queues as an Endpoint. Each component uses the same basic pattern:
Where each component consumes messages off of an input queue, processes the message(s), and then places 1+ messages on an outbound/output queue. The "output" queue then becomes the "input" queue for the next component in the chain. Pretty basic.
I am now trying to roll up my sleeves and provide unit testing for each component using the MockEndpoints provided by Camel's test API. I have been pouring over the javadocs and the few examples on Camel's website, but am having difficulty connecting all the dots.
It seems to me that, for each component, a portion of my unit testing is going to want to accomplish the following three things:
Test to see if there are messages waiting on a particular "input" queue
Pull those messages down and process them
Push new messages to an "output" queue and verify that they made it there
I believe I need to create MockEndpoints for each queue like so:
#EndpointInject(uri = "mock:inputQueue")
protected MockEndpoint intputQueue;
#EndpointInject(uri = "mock:outputQueue")
protected MockEndpoint outputQueue;
So now, in my JUnit test methods, I can set up expectations and interact with these endpoints:
#Test
public final void processMethodShouldSendToOutputQueue()
{
Component comp = new Component();
comp.process();
outputQueue.assertIsSatisfied();
}
I'm just not understanding how to wire everything up correctly:
How do I connect comp to the inputQueue and outputQueue MockEndpoints?
For each MockEndpoint, how do I set up expectations so that assertIsSatisfied() checks that a message is present inside a particular queue, or that a particular queue contains messages?

Adam, there are several ways to do this.
For POJO components, blackbox test them separately from any Camel context/routing to focus on business logic.
If you want to do end-to-end testing of the routes, consider using one of these approaches to validate that each step in the route is satisfied.
use NotifyBuilder to build Exchange validation expressions (somewhat complex to get your head around)
use AdviceWith to dynamically change the route before its run (add Log/Mock endpoints, etc)
I prefer AdviceWith because its very flexible and leverages the familiar MockEndpoints. For a complete example of this, see this unit test
In short, you will create a unit test to inject MockEndpoints into your route and then validate against them as usual...
context.getRouteDefinition("myRouteId").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
// mock all endpoints
mockEndpoints();
}
});
getMockEndpoint("mock:direct:start").expectedBodiesReceived("Hello World");
template.sendBody("direct:start", "Hello World");

Related

Configure Thread Names in Apache Camel

I'm kind of new to Apache Camel and am testing it to use it on my application (replacing already implemented Spring Integration).
I've been searching all over the web, Apache Camel's documentation site and stackoverflow for the last days but can't seem to find an answer on how to configure thread's names in Apache Camel via Java DSL.
I only got to see this and this other question but it only says how to do it via Spring DSL. Same on Apache Camel documentation page.
To give some context:
Right now I'm building two operation flows (first and second) and each one has it's one route.
Both routes read from different ActiveMQ queues, process the messages in a different way and the send the response back to different queues.
I already managed to configure different concurrentConsumers and maxConcurrentConsumers for each route (via properties file).
I would like to assign thread names (or at least patterns, since I have many consumers on each route); in a way that I could have something like "FirstOp-X" and "SecondOp-X" (where X is the thread number).
Here's the code snippet:
public class SampleCamelRouter extends RouteBuilder {
/**
* The first operation name
*/
public static final String FIRST_NAME = "first";
/**
* The second operation name
*/
public static final String SECOND_NAME = "second";
/**
* The ActiveMQ outbound queue format
*/
public static final String OUTBOUND_QUEUE_FORMAT = "activemq:queue:aq.%1$s.response";
/**
* The ActiveMQ inbound queue format
*/
public static final String INBOUND_QUEUE_FORMAT = "activemq:queue:aq.%1$s.request"
+ "?concurrentConsumers={{queue.%1$s.concurrentConsumers}}"
+ "&maxConcurrentConsumers={{queue.%1$s.maxConcurrentConsumers}}";
/*
* (non-Javadoc)
* #see org.apache.camel.builder.RouteBuilder#configure()
*/
#Override
public void configure() throws Exception {
from(String.format(INBOUND_QUEUE_FORMAT, FIRST_NAME))
.unmarshal().json(JsonLibrary.Jackson, FirstRequestMessage.class)
.bean(TestBean.class, "doFirst")
.marshal().json(JsonLibrary.Jackson)
.to(String.format(OUTBOUND_QUEUE_FORMAT, FIRST_NAME));
from(String.format(INBOUND_QUEUE_FORMAT, SECOND_NAME))
.unmarshal().json(JsonLibrary.Jackson, SecondRequestMessage.class)
.bean(TestBean.class, "doSecond")
.marshal().json(JsonLibrary.Jackson)
.to(String.format(OUTBOUND_QUEUE_FORMAT, SECOND_NAME));
}
I used to do it in Spring Integration with something like this (per flow):
#Bean
public static IntegrationFlow setUpFirstFlow() {
final DefaultMessageListenerContainer messageListenerContainer = new DefaultMessageListenerContainer();
messageListenerContainer.setBeanName("FirstOp");
messageListenerContainer.setDestinationName("aq.first.request");
messageListenerContainer.setConcurrentConsumers(concurrentConsumers);
messageListenerContainer.setMaxConcurrentConsumers(maxConcurrentConsumers);
return IntegrationFlows.from(Jms.messageDrivenChannelAdapter(messageListenerContainer))
.transform(new JsonToObjectTransformer(FirstRequestMessage.class))
.handle(TestBean.class, "doFirst")
.transform(new ObjectToJsonTransformer())
.handle(Jms.outboundAdapter(.......)).get();
}
So basically: I created different message listener containers, and that way, I have different thread names per flow.
At any time if a thread is stoped, blocked, inside a thread-dump (or even as simple as printing a log) I could easily see, wich flow that thread belongs to.
I see Apache Camel has some kind of workaround (but not per route, but per camelContext) but only implemented using Spring DSL not Java.
I wouldn't mind to change my configuration to XML files if this per-route-configuration is possible only with Spring.
Please help me, it's kind of a tie-breaker for me right now. For the application I'm building, it's very important to be able to identify each thread isolated; and don't really like the default thread names (Camel (camel-1) thread #27 - JmsConsumer[aa.first.request]) :'-(.
You can set an ExecutorServiceManager per Camel context via org.apache.camel.impl.DefaultCamelContext#setExecutorServiceManager - if you use a DefaultExecutorServiceManager you can set a threadNamePattern.
Alternatively, you can use the Threads DSL and assign a thread pool to a route by using org.apache.camel.model.ProcessorDefinition#threads(int, int, java.lang.String), e.g.
from(String.format(INBOUND_QUEUE_FORMAT, FIRST_NAME))
.threads(1, 2, "first")
...
from(String.format(INBOUND_QUEUE_FORMAT, SECOND_NAME))
.threads(1, 2, "second")
...
Note that using the threads() method would effectively mean that you would be using an asynchronous processing model in Camel.

Invoke route from Processor

I'm using Camel to integrate 2 systems. I have defined different routes and one of the routes consumes from a specific rabbitmq queue and send it to a REST service. Nothing fancy here, the route looks like this:
public class WebSurfingRabbitToRestRoute extends RouteBuilder{
#Override
public void configure() throws Exception {
from("rabbitmq://rabbit_host:port/Rabbit_Exchange").
setHeader("CamelHttpMethod", constant("POST")).
setHeader("Content-Type", constant("application/json")).
bean(TransformResponse.class, "transform").
to("http4://rest_service_host:port/MyRestService).
}
}
As you can see, i process every message before sending it to the rest service since i need to adjust some things. The problem comes when i find out that sometimes (i dont know how or when), the system that publish into rabbit, send 2 messages concatenated at once.
What i expect to get is a simple json like this:
[{field1:value1, field2:value2}]
What i sometimes get is:
[{field1:value1, field2:value2},{field1:value3, field2:value4}]
So when i face this scenario, the rest service im routing the message to, fails (obviously).
In order to solve this, i would like to know if there is a way to invoke a route from inside a processor. From the previous snippet of code you can see that Im calling the transform method, so the idea will be to do something like the following pseudo-code, because after the route is already fired, i cant split the events and send them both within the same route "instance", so i thought about invoking a different route that i can call from here which will send the message2 to the very same rest service.
public class TransformRabbitmqResponse {
public String transform(String body) throws Exception {
// In here i do stuff with the message
// Check if i got 2 messages concatenated
// if body.contains("},{") {
// split_messages
// InvokeDifferentRoute(message2)
//}
}
}
Do you guys think this is possible?
One option (though I am not sure this is the best option) would be to split this up into two different routes using a direct endpoint.
public class WebSurfingRabbitToRestRoute extends RouteBuilder{
#Override
public void configure() throws Exception {
from("rabbitmq://rabbit_host:port/Rabbit_Exchange")
.setHeader("CamelHttpMethod", constant("POST"))
.setHeader("Content-Type", constant("application/json"))
.bean(TransformResponse.class, "transform");
from("direct:transformedResponses")
.to("http4://rest_service_host:port/MyRestService");
}
}
And then in your transform bean, you can use camel Producer Template to publish the transformed payload(s) to your new direct endpoint (assuming you are using json?).
producerTemplate.sendBody("direct:transformedResponses", jsonString);

How can I test that a Camel route calls different endpoints in order?

I use Apache Camel to orchestrate a number of HTTP calls to three different endpoints. This works correctly, but I'd like to unit test this Camel route as well.
I can correctly assert that the three endpoints were called, but I don't know how to verify that they were called in the correct order. I have looked into using MockEndpoint and NotifyBuilder.
How can I verify in a Camel Spring JUnit test that the endpoints were called in the correct order?
Hmm I am not certain if there is an easy way to do this, but you could use the AdviceWithRouteBuilder to weave a bean into your route that puts a counter in the header before each send.
Something like:
weaveById("send1")
.before()
.bean(CounterIncrementor.class);
weaveById("send2")
.before()
.bean(CounterIncrementor.class);
weaveById("send3")
.before()
.bean(CounterIncrementor.class);
..................
List<Exchange> list = getMockEndpoint("mock:extract").getReceivedExchanges();
Exchange exchange = list.get(1);
Message in = exchange.getIn();
in.getHeader("MyCounterHeader", Integer.class);
//TODO verify you received the correct number

Mocking or encapsulate part of camel route

Is there any way to mocking part of camel route?
I build such a route:
from("a").b().signReq().send().validateAns().c().to("d");
but when i run tests, i don't want add signReq().send().validateAns() into route. Any suggestions?
Also, maybe there is a way to encapsulate such part of route into method? It will be great, because i have many routes and many same interaction parts. Best if it can be done without runtime choice/when switches, because i know all conditions in configure phase.
For testing existing routes you can use AdviceWith and advice a route before its being tested.
I propose using weaveById which is the most precise way to replace parts of a route.
For example in the following route
from("direct:start")
.to("mock:foo")
.to("mock:bar").id("bar")
.to("mock:result");
After setting the id of "mock:bar" to "bar" then you can use in your test
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
// weave the node in the route which has id = bar
// and replace it with the following route path
weaveById("bar").replace().multicast().to("mock:a").to("mock:b");
}
});
In you example you can do something like:
from("a").b().to("direct:replace").id("replace").c().to("d");
from("direct:replace").signReq().send().validateAns();
And afterwards advice the route by using:
weaveById("replace").remove();
Of course more ways exist to implement this functionality. For all the options and a full example go to http://camel.apache.org/advicewith.html
Tip: Give extra attention to the part of the code in the example that starts the context!
// we must manually start when we are done with all the advice with
context.start();

How to build a auto reply JMS listener in JUnit (in OpenEJB)

I have a EJB to send a message to JMS queue and wait the reply from it. I want to test the EJB, it's easy to use OpenEJB to do the JUnit test of the EJB. But the problem is this EJB will wait the JMS response to continue process.
Although I can send message in my junit code, but because the EJB is still on-going, I cannot run it before the EJB is completed.
2nd solution is I can initialize a MDB to listen and reply the JMS message form the EJB, but the problem is the MDB must in src\main\java and cannot in src\test\java. The problem is this is just a test code and I should not package it to production environment. (I use Maven)
Or should I use mock object ?
You're on the right track. There area few ways to handle this. Here are a couple tips for unit testing with OpenEJB and Maven.
Test beans
You can write all sorts of EJBs and other testing utilities and have them deployed. All you need is a ejb-jar.xml for the test code like so:
src/main/resources/ejb-jar.xml (the normal one)
src/test/resources/ejb-jar.xml (the testing beans)
As usual the ejb-jar.xml file only needs to contain <ejb-jar/> and nothing more. Its existence simply tells OpenEJB to inspect that part of the classpath and scan it for beans. Scanning the entire classpath is very slow, so this is just convention to speed that up.
TestCase injection
With the above src/test/resources/ejb-jar.xml you could very easily add that test-only MDB and have it setup to process the request in a way that the TestCase needs. But the src/test/resources/ejb-jar.xml also opens up some other interesting functionality.
You could have the TestCase itself do it by declaring references to whatever JMS resources you need and have them injected.
import org.apache.openejb.api.LocalClient;
#LocalClient
public class ChatBeanTest extends TestCase {
#Resource
private ConnectionFactory connectionFactory;
#Resource(name = "QuestionBean")
private Queue questionQueue;
#Resource(name = "AnswerQueue")
private Queue answerQueue;
#EJB
private MyBean myBean;
#Override
protected void setUp() throws Exception {
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
InitialContext initialContext = new InitialContext(p);
initialContext.bind("inject", this); // here's the magic!
}
}
Now you're just one thread away from being able to respond to the JMS message the testcase itself. You can launch off a little runnable that will read a single message, send the response you want, then exit.
Maybe something like:
public void test() throws Exception {
final Thread thread = new Thread() {
#Override
public void run() {
try {
final Connection connection = connectionFactory.createConnection();
connection.start();
final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
final MessageConsumer incoming = session.createConsumer(requestQueue);
final String text = ((TextMessage) incoming.receive(1000)).getText();
final MessageProducer outgoing = session.createProducer(responseQueue);
outgoing.send(session.createTextMessage("Hello World!"));
} catch (JMSException e) {
e.printStackTrace();
}
}
};
thread.setDaemon(true);
thread.start();
myBean.doThatThing();
// asserts here...
}
See
Alternate Descriptors
If you did want to use the MDB solution and only wanted to enable it for just the one test and not all tests, you could define it in a special src/test/resources/mockmdb.ejb-jar.xml file and enable it in the specific test case(s) where it is needed.
See this doc for more information on how to enable that descriptor and the various options of alternate descriptors.
I think you should use mocks for this. If you're sending messages to a real JMS server, listening for them, replying to them, etc. then you're doing something other than a unit test. I'm not going to get into the argument about what that should be called, but I think it's pretty well universally accepted that a unit-test shouldn't be talking to live databases, message queues, etc.
If I've understood your question correct - It's a bad design to have an EJB send a JMS message and then await a response, in fact contradictory to the whole idea of EJB.
You send a JMS message, and then forget about it. You have an MDB to receive the message. If the EJB depends on a response, JMS is not the way to go, but rather use another EJB.
To test the sending, mock the JMS classes, test the MDB separately.
EJB's are designed for synchronous tasks, JMS for asynchronous tasks - if you have to do asynchronous communication to an external system, I suggest you design your system after that, and do proper asynchronous flows. An EJB that sits and waits for a JMS reply is at best an ugly hack, and will not add any good to your system design.
Thanks for David's answer, it's what I want. I know unit test should not depend on other external resource like JMS server. But if I use Maven + OpenEJB, I still can let the test code in a closed environment. It can help to do automatically test with external resource dependency, especially for some old programs which not easy to refactor.
And if you see the following error message in initialContext.bind("inject", this)
Ensure that class was annotated with #org.apache.openejb.api.LocalClient and was successfully discovered and deployed.
One reference is http://openejb.apache.org/3.0/local-client-injection.html, but add "openejb.tempclassloader.skip=annotations" doesn't work for me. Please check this doc OpenEJB Local Client Injection Fails. There is already a patch for it, I think it will be fixed in OpenEJB 3.1.5 or 4.0
Also I've found it is best practice to actually break out your logic in your MDB to a different class. This isolates your business logic from being in an MDB and allows you to expose your logic as more than one way (MDB, EJB, Web Service, POJO, etc.). It also allows you to more easily test your business logic without the need to test the protocol (JMS in this case).
As for testing JMS, mocking may be the better choice. Or if you really need to test the protocol "in container" look at using something like the JBoss Microcontainer (I believe you can get this packaged with some of the JBoss projects like Seam). Then you can fire up a mini-container for testing things like EJB and JMS.
But overall, it is best to avoid having to need a container unless absolutely necessary. That's why separating your business logic from your implementation logic (even if you don't use mocks) is a good practice.

Categories