I am trying to write Junit5 test case for apache camel router which has the DB operations.
Use case : There is an API call which will give me the data and my router is inserting those data to local database.
I have to write the test case to check the count from the database and compare it with the api data count which should match.
And the Database connection as well
My test class needs to get the count from the database and api to compare if both matches the assert satisfied .
Please help me to write test case for the same , Please refer the below for junit test case code.
Thanks in advance.
import org.apache.camel.CamelContext;
import org.apache.camel.EndpointInject;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.AdviceWith;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.http.base.HttpOperationFailedException;
import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTestContextBootstrapper;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.BootstrapWith;
#CamelSpringBootTest
#ActiveProfiles("mock")
#BootstrapWith(SpringBootTestContextBootstrapper.class)
public class TestingPersonStoringDatabase {
#Autowired
protected CamelContext camelContext;
#Autowired
private ProducerTemplate template;
#EndpointInject("mock:purePersonsEndpoint")
private MockEndpoint personsMock;
#EndpointInject("mock:storePersons")
private MockEndpoint mock;
#BeforeEach
public void setup() throws Exception {
AdviceWith.adviceWith(camelContext, "storePersonsRoute",
a -> a.weaveAddLast().to("sql:select count(*) as results from persons").setBody()
.simple("${body[0][RESULTS]}").log("RESULTS: ${body}").to("mock:storePersons"));
camelContext.start();
}
#Test
public void testPersons() throws Exception {
personsMock.whenAnyExchangeReceived(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
throw new HttpOperationFailedException("", 400, "Simulated API error", null, null, null);
}
});
//MockEndpoint.assertIsSatisfied(camelContext);
mock.expectsMessageCount(142);
personsMock.expectsMessageCount(142);
mock.assertIsSatisfied();
}
}
storePersonsRoute this router has a inserting data in the database
Related
I have a Camel route that moves messages from a jms queue to another. That route is by default stopped and it is started by a call to a jetty route using controlBus.
As I need to move the messages on demand, once the source jms queue is empty, I need to disable the "mover" route, so messages that arrive later are not processed until the "mover" route is activated again.
Is there a way to achieve that?
You could try to obtain the queue message count using JmsTemplate and then shutting down the route from another thread using a processor. Downsides might be a dependency to org.springframework.jms.core.JmsTemplate and some gotchas related to it.
package com.example;
import java.util.Collections;
import javax.jms.ConnectionFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.junit.EmbeddedActiveMQBroker;
import org.apache.camel.CamelContext;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.ServiceStatus;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.jms.JmsComponent;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.jms.core.JmsTemplate;
public class QueueConsumerTests extends CamelTestSupport {
#Rule
public EmbeddedActiveMQBroker broker = new EmbeddedActiveMQBroker();
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
"vm://localhost?broker.persistent=false");
#Test
public void stopesQueueListenerRouteAfterConsumingAllMessages() throws Exception {
MockEndpoint jmsMockEndpoint = getMockEndpoint("mock:jmsMockEndpoint");
jmsMockEndpoint.expectedMessageCount(5);
for (int i = 1; i <= 5; i++) {
template.sendBody("direct:test", "Message " + i);
}
context().getRouteController().startRoute("queueListener");
Thread.sleep(5000);
ServiceStatus routeStatus = context().getRouteStatus("queueListener");
assertEquals(routeStatus, ServiceStatus.Stopped);
jmsMockEndpoint.assertIsSatisfied();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:test")
.to("jms:queue:test");
from("jms:queue:test")
.routeId("queueListener")
.autoStartup(false)
.log("message from queue: ${body}")
.to("mock:jmsMockEndpoint")
.setBody().exchange(e -> {
int messageCount = jmsTemplate.browse("test", (session, browser) -> {
return Collections.list(browser.getEnumeration()).size();
});
return messageCount;
})
.filter(body().isEqualTo(0))
.to("seda:stopPolling")
.end();
from("seda:stopPolling?concurrentConsumers=1&multipleConsumers=false")
.log("stop polling")
.process(e -> e.getContext().getRouteController().stopRoute("queueListener"))
.setProperty("stopped").constant(false)
.loopDoWhile(exchangeProperty("stopped").isEqualTo(false))
.delay(100)
.setProperty("stopped").exchange(e -> {
return e.getContext().getRouteStatus("queueListener").isStopped();
})
.end()
.log("stopped queueListener");
}
};
}
#Override
protected CamelContext createCamelContext() throws Exception {
CamelContext context = new DefaultCamelContext();
JmsComponent jmsComponent = new JmsComponent();
jmsComponent.setConnectionFactory(connectionFactory);
context.addComponent("jms", jmsComponent);
return context;
}
}
Alternative could be to use timer with poll-enrich with timeout and shutdown the route if it results in null body. This however is slower and possibly less robust due to how you need to specify frequency of polling and timeout.
package com.example;
import javax.jms.ConnectionFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.junit.EmbeddedActiveMQBroker;
import org.apache.camel.CamelContext;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.ServiceStatus;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.jms.JmsComponent;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Rule;
import org.junit.Test;
public class QueueConsumerTests2 extends CamelTestSupport {
#Rule
public EmbeddedActiveMQBroker broker = new EmbeddedActiveMQBroker();
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
"vm://localhost?broker.persistent=false");
#Test
public void stopesQueueListenerRouteAfterConsumingAllMessages() throws Exception {
MockEndpoint jmsMockEndpoint = getMockEndpoint("mock:jmsMockEndpoint");
jmsMockEndpoint.expectedMessageCount(5);
for (int i = 1; i <= 5; i++) {
template.sendBody("direct:test", "Message " + i);
}
context().getRouteController().startRoute("queueListener");
Thread.sleep(10000);
ServiceStatus routeStatus = context().getRouteStatus("queueListener");
assertEquals(routeStatus, ServiceStatus.Stopped);
jmsMockEndpoint.assertIsSatisfied();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:test")
.to("jms:queue:test");
from("timer:moverTimer?period=500")
.routeId("queueListener")
.autoStartup(false)
.pollEnrich("jms:queue:test", 1000)
.choice()
.when(body().isNotNull())
.log("message from queue: ${body}")
.to("mock:jmsMockEndpoint")
.otherwise()
.to("seda:stopPolling")
.end();
from("seda:stopPolling?concurrentConsumers=1&multipleConsumers=false")
.log("stop polling")
.process(e -> e.getContext().getRouteController().stopRoute("queueListener"))
.setProperty("stopped").constant(false)
.loopDoWhile(exchangeProperty("stopped").isEqualTo(false))
.delay(100)
.setProperty("stopped").exchange(e -> {
return e.getContext().getRouteStatus("queueListener").isStopped();
})
.end()
.log("stopped queueListener");
}
};
}
#Override
protected CamelContext createCamelContext() throws Exception {
CamelContext context = new DefaultCamelContext();
JmsComponent jmsComponent = new JmsComponent();
jmsComponent.setConnectionFactory(connectionFactory);
context.addComponent("jms", jmsComponent);
return context;
}
}
Now as a disclaimer haven't tested these patterns thoroughly so there might be some edge cases (and better ways to do this). There's also the fact that Camel documentation uses java.lang.Thread to stop route inside a processor instead of using seda consumer so there might be something in to that.
Use of Thread.sleep in unit test is also quite messy and not something I would recommend unless you just want to quickly experiment on something with camel.
Used Dependencies:
org.apache.camel/camel-core/2.24.2
org.apache.camel/camel-jms/2.24.2
Test scope:
org.apache.camel/camel-test/2.24.2
org.apache.activemq.tooling/activemq-junit/5.16.3
org.apache.activemq/activemq-broker/5.16.3
I was trying to get Snmp v3 traps using Apache Camel SNMP component. I got both v1 and v2 traps but I am not able to get the v3.
import java.util.Date;
import java.util.Objects;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.main.Main;
import org.apache.camel.main.MainSupport;
import org.apache.camel.main.MainListenerSupport;
import org.apache.camel.Processor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.camel.component.snmp.*;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.CamelContext;
import org.apache.camel.StartupListener;
import org.snmp4j.security.*;
import org.snmp4j.PDU;
import java.util.*;
import org.snmp4j.smi.*;
import org.snmp4j.mp.*;
import org.apache.camel.model.dataformat.*;
public final class Snmp2 {
public static void main(String[] args) throws Exception {
// CamelContext contains methods to add routes, components etc
CamelContext context = new DefaultCamelContext();
// work with Snmp component
context.addComponent("snmp", new SnmpComponent());
// Add the route
context.addRoutes(new RouteBuilder() {
public void configure() {
// When a SNMP TRAP is received on port 162 from any source..
System.out.println(new OctetString("MD5DES"));
System.out.println(SnmpConstants.version3);
System.out.println(AuthMD5.ID);
// from("snmp:0.0.0.0:162?protocol=udp&type=TRAP&securityName="+new OctetString("MD5DES")+"&snmpVersion="+SnmpConstants.version3+"&securityLevel="+SecurityLevel.NOAUTH_NOPRIV).process(myProcessor);
from("snmp:0.0.0.0:162?protocol=udp&type=TRAP&securityName="+new OctetString("MD5DES")+"&snmpVersion="+SnmpConstants.version3+"&securityLevel="+SecurityLevel.AUTH_PRIV+"&authenticationPassphrase=UserName&privacyPassphrase=PasswordUser&authenticationProtocol=MD5&privacyProtocol=DES").process(myProcessor).to("file://D:/traps.log");
}
});
// Get notification when the context is started..
context.addStartupListener(new StartupListener(){
public void onCamelContextStarted(CamelContext context, boolean alreadyStarted)
{
System.out.println("Camel context is started");
}
});
// Start the context
context.start();
// Sleep for sometime, so that the context can keep running
// otherwise the program exits
Thread.sleep(1000000);
context.stop();
}
public static Processor myProcessor = new Processor() {
public void process(Exchange trap) throws Exception {
System.out.println(trap.getIn().getHeaders());
// getIn() means get the inbound message
// Since we are using Snmp, we get SnmpMessage object, we need to typecast from Message
SnmpMessage msg=(SnmpMessage)trap.getIn();
// PDU refers to SNMP4J PDU class, the camel SNMP component internally uses it
PDU pdu=msg.getSnmpMessage();
// Get the VariableBindings (i.e.) the list of OID and value pairs
// print them each
pdu.getVariableBindings().forEach(System.out::println);
/*
Vector<? extends VariableBinding> v=pdu.getVariableBindings();
for(VariableBinding b:v)
{
System.out.println(b.getSyntax());
}
*/
}
};
public static class PrintBean {
// This also does the same thing
public void print(String msg)
{
System.out.println(msg);
}
}
}
While I am sending the trap, the trap is being sent successfully. But I am not able to get into the myProcessor.
I'm trying to test the onException(JsonProcessingException.class) route in the following class (please don't mind its name, I've cut some code out for clarity):
import org.apache.camel.Exchange;
import org.apache.camel.LoggingLevel;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.rest.RestBindingMode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonProcessingException;
import pl.muni.camel.sample.customer.domain.CustomerData;
import pl.muni.camel.sample.customer.route.processor.CreateCustomerErrorResponseProcessor;
import pl.muni.camel.sample.customer.route.processor.CreateCustomerOkResponseProcessor;
#Component
public class SendCustomerDataToQueueRoute extends RouteBuilder {
#Value("${http.rest.listener.host}")
private String restListenerHost;
#Value("${http.rest.listener.port}")
private int restListenerPort;
#Override
public void configure() {
restConfiguration()
.component("restlet")
.dataFormatProperty("prettyPrint", "true")
.host(restListenerHost)
.port(restListenerPort);
rest("/rest/v1/customer")
.post("/create")
.bindingMode(RestBindingMode.json)
.skipBindingOnErrorCode(false)
.consumes("application/json")
.type(CustomerData.class)
.produces("application/json")
.route().id("acceptCreateCustomerRequest")
.from("direct:acceptRequest")
.to("direct:processRequest");
onException(JsonProcessingException.class)
.handled(true)
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(400))
.to("direct:processException");
onException(Exception.class)
.handled(true)
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(500))
.to("direct:processException");
from("direct:processRequest").routeId("processCreateCustomerRequest")
.log("Received customer data: ${body}")
.process(new CreateCustomerOkResponseProcessor()).id("createOkResponse");
from("direct:processException").routeId("processCreateCustomerException")
.log(LoggingLevel.ERROR, "${exception.stacktrace}").id("logExceptionStackTrace")
.process(new CreateCustomerErrorResponseProcessor()).id("createErrorResponse");
}
}
I want to intercept the exchange after createErrorResponse processor and run some assertions on it. So far I've come up with this code, in which I weave in a mock endpoint after direct:processException endpoint:
import java.util.List;
import org.apache.camel.CamelContext;
import org.apache.camel.EndpointInject;
import org.apache.camel.Exchange;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.spring.CamelSpringBootRunner;
import org.apache.camel.test.spring.EnableRouteCoverage;
import org.apache.camel.test.spring.MockEndpointsAndSkip;
import org.apache.camel.test.spring.UseAdviceWith;
import org.assertj.core.api.Assertions;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.annotation.DirtiesContext;
import pl.muni.camel.sample.customer.infrastructure.rest.CreateCustomerResponse;
#UseAdviceWith
#MockEndpointsAndSkip("restlet*")
#EnableRouteCoverage
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#SpringBootTest
#ComponentScan("pl.muni.camel.sample.customer")
#RunWith(CamelSpringBootRunner.class)
public class SendCustomerDataToQueueIntegrationTest {
#Produce
private ProducerTemplate producerTemplate;
#Autowired
private CamelContext context;
#EndpointInject(uri = "mock:error")
private MockEndpoint errorEndpoint;
#Before
public void setUp() throws Exception {
context.getRouteDefinition("processCreateCustomerRequest").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() {
weaveByToUri("direct:processException")
.after()
.to("mock:error");
}
});
context.start();
}
#After
public void tearDown() throws Exception {
context.stop();
}
#Test
public void shouldReturnHttpStatus400ForInvalidJson() throws InterruptedException {
// given
final String customerDataString = "{\"firstName\": \"aaa\", \"lastname\": \"bbb\"}";
//when
producerTemplate.sendBody("direct:acceptRequest", customerDataString);
//then
errorEndpoint.expectedHeaderReceived(Exchange.HTTP_RESPONSE_CODE, 400);
errorEndpoint.assertIsSatisfied();
final List<Exchange> exchanges = errorEndpoint.getExchanges();
Assertions.assertThat(exchanges).hasSize(1);
final Exchange exchange = exchanges.get(0);
final CreateCustomerResponse response = exchange.getIn().getBody(CreateCustomerResponse.class);
Assertions.assertThat(response.isSuccess()).isFalse();
Assertions.assertThat(response.getErrorMessage()).startsWith("UnrecognizedPropertyException: Unrecognized field \"lastname\"");
}
}
Unfortunately, the Exchange.HTTP_RESPONSE_CODE header somehow disappears during the test and the assertion on errorEndpoint fails. I ran the test with debugger and breakpoint set within CreateCustomerErrorResponseProcessor class and there the header was still available.
Is there another way to set up the test and be able to retrieve the header or could this be a bug?
The URI you are weaving ("direct:processException") in your unit test is attached to a wrong route definition.
It should be:
context.getRouteDefinition("processCreateCustomerException").adviceWith(...)
(and not "processCreateCustomerRequest")
Suppose I have the following camel route:
.from("direct:start")
.log("received ${body} message")
.to("mock:end");
How would you test that the message "received Camel rocks!" message is logged when you send a "Camel rocks!" message to the direct:start endpoint
I would read the written file. Or add a custom appender to the logging system and assert that it received the message.
Or check Camel's internal unit tests.
But what exactly are you trying to achieve?
You are supposed to test your application and not the frameworks you are using.
I would not test the actual logging part, but why not save the data you are interested in a property or header, and then in your unit test assert that the value of that property or header must be such and such?
Here is a way to test this which I admit is a bit too invasive. It would have been much easier if AdviceWithBuilder would have been added something like replaceWith(ProcessDefinition replacement).
Here is the working example:
package com.my.org.some.pkg;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.testng.CamelTestSupport;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.springframework.test.util.ReflectionTestUtils;
import org.testng.annotations.Test;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class CamelLoggingTest extends CamelTestSupport {
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start").id("abc")
.log("received ${body} message")
.to("mock:stop");
}
};
}
#Test
public void shouldLogExpectedMessage() throws Exception {
Logger logger = Mockito.mock(Logger.class);
context.getRouteDefinition("abc").adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
ReflectionTestUtils.setField(context.getRouteDefinition("abc")
.getOutputs().get(0), "logger", logger);
}
});
when(logger.isInfoEnabled()).thenReturn(true);
sendBody("direct:start", "Camel rocks!");
verify(logger).info("received Camel rocks! message");
}
}
I have a simple Camel route test class that I have expanded to use a placeholder for an endpoint.
import org.apache.camel.CamelContext;
import org.apache.camel.EndpointInject;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.component.properties.PropertiesComponent;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
import java.util.Properties;
public class SimpleTestRoute extends CamelTestSupport {
#EndpointInject(uri = "{{test.route.out}}")
protected MockEndpoint resultEndpoint;
#Produce(uri = "direct:start")
protected ProducerTemplate template;
#Test
public void test() throws Exception {
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start").to("{{test.route.out}}");
}
};
}
#Override
protected CamelContext createCamelContext() throws Exception {
Properties props = new Properties();
props.setProperty("test.route.out", "mock:result");
CamelContext context = super.createCamelContext();
PropertiesComponent pc = context.getComponent("properties", PropertiesComponent.class);
pc.setOverrideProperties(props);
return context;
}
}
However, when I run I get
org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint: {{test.route.out}} due to: Property with key [test.route.out] not found in properties from text: {{test.route.out}}
at ...
Caused by: java.lang.IllegalArgumentException: Property with key [test.route.out] not found in properties from text: {{test.route.out}}
at ...
I have tried various combinations of brace locations without progress. Now I suspect the problem is that the override is not being set but I cannot see what I am doing wrong or how to resolve it.
FWIW, I am running camel 2.13.1