I want to test multiple camel RouteBuilder in a single unit test
what I have:
Custom camel processor that changes state
public class MyProcessor implements Processor {
MyState state;
public MyProcessor(MyState state) {this.state = state;}
#Override
public void process(Exchange exchange) throws Exception {
state.setState(state.getState() + 5);
}}
Two simple RouteBuilders: first routes messages from "direct:start" to "direct:endroute1" second pick up messages from "direct:endroute1" and routes somewhere "mock:endroute2"
public class MyRouteBuilder1 extends RouteBuilder {
MyState state;
public MyRouteBuilder1(MyState state) {this.state = state;}
#Override
public void configure() throws Exception {
from("direct:start").process(new MyProcessor(state)).to("direct:endroute1");
}}
public class MyRouteBuilder2 extends RouteBuilder {
MyState state;
public MyRouteBuilder2(MyState state) {this.state = state;}
#Override
public void configure() throws Exception {
from("direct:endroute1").process(new MyProcessor(state)).to("mock:endroute2");
}}
Writing unit test for a single route builder is straightforward:
public class MyTest extends CamelTestSupport {
MyState state = new MyStateImpl();
#EndpointInject(uri = "mock:result")
protected MockEndpoint resultEndpoint;
#Test
public void testSingleRoute() throws Exception {
resultEndpoint.expectedMessageCount(1);
template.sendBody("direct:start", new Object());
assertTrue(state.getState() == 5);
resultEndpoint.assertIsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() {
return new MyRouteBuilder1(state) {
public void configure() throws Exception{
super.configure();
from("direct:endroute1").to("mock:result");
}
};
}
}
What I really want to do is somehow to override CamelTestSupport.createRouteBuilder that will test whole chain of message processing from direct:start to mock:endroute2. As a result state.getState() should be 10
Try to override method:
protected RouteBuilder[] createRouteBuilders() {...}
from CamelTestSupport. It's available since version 2.17
you can just add multiple RouteBuilders to the context using the context.addRoutes(RouteBuilder) API
see this unit test for an example:
https://svn.apache.org/repos/asf/camel/trunk/camel-core/src/test/java/org/apache/camel/builder/AddRoutesAtRuntimeTest.java
You could use one RouteBuilder including the routes of multiple other RouteBuilders.
Related
I saw in another post how manually adding the camel context and starting it should work, but it hasn't for me. I double checked the from, and to paths and they seem to be correct. Not sure why it's not calling the method and would appreciate some advice
public class CsvRouteBuilder extends DdsRouteBuilder {
private CsvConverterProcessor csvConverterProcessor;
private CamelContext camelContext;
#Autowired
public CsvRouteBuilder(CsvConverterProcessor csvConverterProcessor) throws Exception {
this.csvConverterProcessor = csvConverterProcessor;
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("{{input.files.csv}}")
.routeId("CSVConverter")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println("hitting");
}
})
.to("{{output.files.csv}}");
}
});
camelContext.start();
}
The processor is not called simply because your route is not properly declared such that Spring Boot is not aware of it.
The proper way is to make your class extend RouteBuilder to define your route(s) and annotate your class with #Component to mark it as candidate for auto-detection when using annotation-based configuration and classpath scanning.
Your code should rather be something like this:
#Component
public class CsvRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("{{input.files.csv}}")
.routeId("CSVConverter")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println("hitting");
}
})
.to("{{output.files.csv}}");
}
}
I am writing a JUnit test case for a Route class. I'm facing a problem while mocking ServiceClass inside the Processor class.
public class SaveRouteTest extends CamelTestSupport {
private Exchange exchange;
protected ProducerTemplate template;
private SaveRequestBuilder saveRequestBuilder;
private SaveRoute route;
private SaveProcessor saveProcessor;
private ApplicationContext springContext = createApplicationContext();
#Mock
SaveServiceClient saveServiceClient;//Not able to mock this class
#BeforeClass
public void prepareTestCamelContext() throws Exception {
route = springContext.getBean("saveRoute", saveRoute.class);
saveProcessor = springContext.getBean("saveProcessor",
SaveProcessor.class);
saveRequestBuilder = springContext.getBean("saveRequestBuilder",
SaveRequestBuilder.class);
}
#BeforeMethod
public void init() throws SQLException, ServiceException {
MockitoAnnotations.initMocks(this);
exchange = new DefaultExchange(context);
}
#Override
protected RouteBuilder[] createRouteBuilders() throws Exception {
template = context.createProducerTemplate();
return new RouteBuilder[]{route};
}
#Test
public void testHotelCommitTransactionRouteSuccessReturn() throws
Exception {
when(saveServiceClient.invokeServiceWithName(anyObject()).
thenReturn("Response");
exchange.getIn().setBody("Request detail");
exchange = template.send("direct:SaveRoute",exchange);
}
protected ApplicationContext createApplicationContext() {
return new ClassPathXmlApplicationContext("classpath*:config/spring/testContext.xml");
}
}
#Component
public class SaveRoute extends SPRouteBuilder {
#Autowired
private SaveProcessor saveProcessor;
#Override
public void configure() throws Exception {
from("direct:SaveRoute")
.routeId("save")
.to("direct:ProcessSaveFlow")
.end();
from("direct:ProcessSaveFlow")
.process(saveProcessor)
.end();
}
}
public class SaveProcessor implements Processor {
#Autowired
SaveServiceClient saveServiceClient;
#Override
public void process(Exchange exchange) throws Exception {
//This line of code not able to mock
String response = saveServiceClient.invokeServiceWithName(exchange);
exchange.getIn().setBody(response);
}
}
How to resolve mocking of saveServiceClient.invokeServiceWithName? The debugger is always going inside this method. I tried using both mock objects and an injected mock. I can't make the method call directly.
You are creating a mock object, however you are not injecting it anywhere (normally you are doing it with #InjectMocks annotation - read about it).
I think there are several possibilities:
Provide a #MockBean object, which will be considered as a bean candidate in context.
There is a code example for mocking beans.
#RunWith ( CamelSpringRunner.class )
#SpringBootTest
public class RouteBuilderTest extends CamelSpringTestSupport {
#Autowired
private ApplicationContext applicationContext;
#MockBean
private ServiceClient serviceClient;
#Override
public void setUp() throws Exception {
MockitoAnnotations.initMocks( this );
super.setUp();
}
#Override
public void tearDown() {
}
#Test
public void test() {
when( serviceClient.doStuff() ).thenReturn( "mockedResponse" );
}
}
Mock SaveProcessor and inject it to Route class - you shouldn't take care of ServiceClient, because you are trying to test too much. Tests for SaveProcessor should be separated, tests for route don't need this logic.
I trying to test the camel output as Object but which fails to get exchange object.This is where it fails Customer resultCustomer = processActs.getExchanges().get(0).getIn().getBody(Customer.class). Please help me to solve this.I referred this Right way to test my object in Camel
Customer POJO:
public class Customer {
private String firstName;
private String lastName;
// getters and setters
#Override
public String toString(){
return firstName +":::" + lastName;
}
}
Test Route:
public class FileTest4 extends CamelTestSupport {
#EndpointInject(uri = "direct:teststart")
private Endpoint start;
#EndpointInject(uri = "mock:direct:processActs")
private MockEndpoint processActs;
#EndpointInject(uri = "mock:direct:write2File")
private MockEndpoint write2File;
#EndpointInject(uri = "mock:end")
private MockEndpoint mockEndResult;
#Override
public boolean isUseAdviceWith() {
return true;
}
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("file:/var/file.log&noop=true").routeId("MY_ROUTE").to("direct:processActs");
from("direct:processActs").process(exchange -> {
List<Customer> customers = new ArrayList<>();
customers.add(new Customer("F1", "L1"));
customers.add(new Customer("F2", "L2"));
customers.add(new Customer("F3", "L3"));
exchange.getOut().setBody(customers);
}).to("direct:write2File");
from("direct:write2File").split(simple("${body}")).log("Content: ${body}");
}
};
}
#Override
protected void doPostSetup() throws Exception {
context.getRouteDefinition("MY_ROUTE").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith("direct:teststart");
weaveAddLast().to("mock:end");
}
});
context.start();
}
#Test
public void testUnmarshal() throws Exception {
mockEndResult.expectedMessageCount(1);
// ArrayIndex Exception here exchanges list is empty
Customer resultCustomer = processActs.getExchanges().get(0).getIn().getBody(Customer.class);
assertEquals(resultCustomer.toString(),"F1:::L1");
write2File.expectedBodiesReceived("F1:::L1", "F3:::L3", "F2:::L2");
template.sendBody("direct:teststart", new File("src/test/resources/test.txt"));
mockEndResult.assertIsSatisfied();
}
}
It looks like you're inspecting the mock endpoint before you've actually sent any exchanges. Try moving the check to the end of the test, e.g.:
#Test
public void testUnmarshal() throws Exception {
mockEndResult.expectedMessageCount(1);
write2File.expectedBodiesReceived("F1:::L1", "F3:::L3", "F2:::L2");
template.sendBody("direct:teststart", new File("src/test/resources/test.txt"));
mockEndResult.assertIsSatisfied();
Customer resultCustomer = processActs.getExchanges().get(0).getIn().getBody(Customer.class);
assertEquals(resultCustomer.toString(),"F1:::L1");
}
UPDATE
On closer inspection, I think you've got your mocks muddled up. Judging by the assertions you want to check that three customers are written out. However your mocks aren't set up for this.
mock:end is added to the end of MY_ROUTE but that will only ever see the entire customer list returned by the processor in direct:processActs
Also the mocks you declare with #EndpointInject don't get involved in the route because you don't actually mock the real endpoints. You can remove all of them apart from mockEndResult.
The following test does pass.
#Test
public void testUnmarshal() throws Exception {
mockEndResult.expectedMessageCount(1);
template.sendBody("direct:teststart", new File("src/test/resources/test.txt"));
mockEndResult.assertIsSatisfied();
#SuppressWarnings("unchecked")
List<Customer> customers = mockEndResult.getExchanges().get(0).getIn().getBody(List.class);
assertEquals(customers.get(0).toString(), "F1:::L1");
assertEquals(customers.get(1).toString(), "F2:::L2");
assertEquals(customers.get(2).toString(), "F3:::L3");
}
That might not be what you want to test, though. Instead you could weave the mock endpoint in to the splitter, then you'd be able to assert individual customers.
#Override
protected void doPostSetup() throws Exception {
context.getRouteDefinition("MY_ROUTE").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith("direct:teststart");
}
});
// give direct:write2File the id 'splitter' to be able to advice it
context.getRouteDefinition("splitter").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveByType(LogDefinition.class).after().to("mock:end");
}
});
context.start();
}
#Test
public void testUnmarshal() throws Exception {
mockEndResult.expectedMessageCount(3);
mockEndResult.expectedBodiesReceived("F1:::L1", "F2:::L2", "F3:::L3");
template.sendBody("direct:teststart", new File("src/test/resources/test.txt"));
mockEndResult.assertIsSatisfied();
}
I followed guide here, and I was successfully able to configure a producer on my bean endpoint like this:
#Produce( uri = "activemq:foo" )
private MyListener myListener;
MyListener is:
public interface MyListener {
#InOnly
public void send( String message );
}
and my bean:
public class MyBeanEndpoint {
#Produce( uri = "activemq:foo" )
private MyListener myListener;
#Handler
public void doSomething( final Object body ) {
...
}
public void setMyListener( final MyListener myListener ) {
this.myListener = myListener;
}
Now, how can I test this?
I mean: my test extends CamelTestSupport and I configured my routes with
#Override
public RouteBuilder createRouteBuilder() {
return new RouteBuilder() { ... }
That is: I've reproduced camel context, but I've NO spring context configured and I want (if possible) to avoid instantiating it.
How can I mock producer or make Camel instantiate and inject this bean into my bean endpoint?
What is the best way to test such situation using Apache Camel features like CamelTestSupport and similar utilities?
My reference test is:
public class Test extends CamelTestSupport {
private static BeanEndpoint beanEndpoint
#BeforeClass
public static void init() {
beanEndpoint.setActivemqMyListener( ??? );
}
#Override
public CamelContext createCamelContext() {
context = new DefaultCamelContext();
context.addComponent( "activemq", new SedaComponent() );
return context;
}
#Override
public RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from( "activemq:foo" )
.to( "mock:out" );
}
};
}
#Test
public void testFooQueue() throws Exception {}
Let Camel create your bean, then the various dependency injection and whatnot is configured for you.
private static BeanEndpoint beanEndpoint
...
beanEndpoint = camelContext.getInjector().newInstance(BeanEndpoint.class);
I´m very new on the camel world, that is why I´m asking for your help.
Let me tell you what I would like to do:
I have this basic Camel standalone project:
package maventest1;
public class JmsToSql {
private Main main;
public static void main(String[] args) throws Exception {
JmsToSql example = new JmsToSql();
example.boot();
}
public void boot() throws Exception {
main = new Main();
main.enableHangupSupport();
main.bind("foo", new MyBean());
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
main.bind("test-jms",JmsComponent.jmsComponentAutoAcknowledge(connectionFactory));
main.addRouteBuilder(new MyRouteBuilder());
main.run();
}
private static class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("timer:foo?delay=2000")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
//NOT SURE THIS IS THE RIGHT WAY
from("test-jms:queue:order1")
.to("test-jms:queue:order2");
}
})
.beanRef("foo");
}
}
public static class MyBean {
public void callMe() {
System.out.println("MyBean.calleMe method has been called");
}
}
}
All I want to do is read all the messages from an activeMQ queue and pass them into another queue. Does anybody know how I can do this?
Thanks in advance =D
Just do a route from JMS to JMS
private static class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("test-jms:queue:order1")
.to("test-jms:queue:order2");
}
As you are new to Camel, I recommend to also read this article first
http://java.dzone.com/articles/open-source-integration-apache
And if you want to have great documentation and tutorials, then pickup one of the Camel books
http://camel.apache.org/books