CamelTestSupport read placeholders from yml file - apache-camel

I am trying to test my Camel Routes using CamelTestSupport. I have my routes defined in a class like this
public class ActiveMqConfig{
#Bean
public RoutesBuilder route() {
return new SpringRouteBuilder() {
#Override
public void configure() throws Exception {
from("activemq:{{push.queue.name}}").to("bean:PushEventHandler?method=handlePushEvent");
}
};
}
}
And my test class look like this
#RunWith(SpringRunner.class)
public class AmqTest extends CamelTestSupport {
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new ActiveMqConfig().route();
}
#Override
protected Properties useOverridePropertiesWithPropertiesComponent() {
Properties properties = new Properties();
properties.put("pim2.push.queue.name", "pushevent");
return properties;
}
protected Boolean ignoreMissingLocationWithPropertiesComponent() {
return true;
}
#Mock
private PushEventHandler pushEventHandler;
#BeforeClass
public static void setUpClass() throws Exception {
BrokerService brokerSvc = new BrokerService();
brokerSvc.setBrokerName("TestBroker");
brokerSvc.addConnector("tcp://localhost:61616");
brokerSvc.setPersistent(false);
brokerSvc.setUseJmx(false);
brokerSvc.start();
}
#Override
protected JndiRegistry createRegistry() throws Exception {
JndiRegistry jndi = super.createRegistry();
MockitoAnnotations.initMocks(this);
jndi.bind("pushEventHandler", pushEventHandler);
return jndi;
}
#Test
public void testConfigure() throws Exception {
template.sendBody("activemq:pushevent", "HelloWorld!");
Thread.sleep(2000);
verify(pushEventHandler, times(1)).handlePushEvent(any());
}}
This is working perfectly fine. But I have to set the placeholder {{push.queue.name}} using useOverridePropertiesWithPropertiesComponent function. But I want it to be read from my .yml file.
I am not able to do it. Can someone suggest.
Thanks

Properties are typically read from .properties files. But you can write some code that read the yaml file in the useOverridePropertiesWithPropertiesComponent method and put them into the Properties instance which is returned.

Thank Claus.
I got it working by doing this
#Override
protected Properties useOverridePropertiesWithPropertiesComponent() {
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
try {
PropertySource<?> applicationYamlPropertySource = loader.load(
"properties", new ClassPathResource("application.yml"),null);
Map source = ((MapPropertySource) applicationYamlPropertySource).getSource();
Properties properties = new Properties();
properties.putAll(source);
return properties;
} catch (IOException e) {
LOG.error("Config file cannot be found.");
}
return null;
}

Related

Unit test Apache Camel specific routes by filtering (Model#setRouteFilter)

How to include only certain routes in my unit test. For example, how do I enable only my-translation-route.
public class TestRoute extends RouteBuilder {
#Override
public void configure() {
from("ftp://my-ftp-server:21/messages")
.routeId("my-inbound-route")
.to("direct:my-translation-route");
from("direct:my-translation-route")
.routeId("my-translation-route")
.bean(MyBean.class)
.to("direct:my-outbound-route");
from ("direct:my-outbound-route")
.routeId("my-translation-route")
.to("http://my-http-server:8080/messages");
}
}
I tried with Model#filterRoutes but this did not work. All routes were loaded.
class TestRouteTest extends CamelTestSupport {
#Override
protected RoutesBuilder createRouteBuilder() {
return new TestRoute();
}
#Override
public boolean isUseAdviceWith() {
return true;
}
#Test
void testIfItWorks() throws Exception {
context.setRouteFilterPattern("my-translation-route", null);
AdviceWith.adviceWith(context, "my-translation-route", a -> {
a.mockEndpointsAndSkip("direct:my-outbound-route");
});
context.start();
getMockEndpoint("mock:direct:my-outbound-route").expectedBodyReceived().expression(constant("Hahaha! 42"));
template.sendBodyAndHeaders("direct:my-translation-route", "42", null);
assertMockEndpointsSatisfied();
}
}
I got it working with the override of CamelTestSupport#getRouteFilterIncludePattern, e.g.:
#Override
public String getRouteFilterIncludePattern() {
return "direct:my-translation-route";
}
But then this is set for all tests in this test class.
Possible (stupid) solution : set a conditional auto startup for your routes, whose value depends on a (Camel or JVM) property that you can set with a particular value during the unit tests:
public class TestRoute extends RouteBuilder {
#PropertyInject(name="productionMode", defaultValue="true")
private boolean productionMode;
#Override
public void configure() {
from("ftp://my-ftp-server:21/messages")
...
.autoStartUp(productionMode); // <=here
}
}
There are various ways to override properties during your tests. See https://camel.apache.org/components/3.17.x/properties-component.html

Apache Camel Generic Router - pass exchange properties to static class methods

I am trying to create a generic router whose processor and other attributes are populated from a static class. Here is sample code.
public class GenericRouter extends RouteBuilder( {
#Override
public void configure() throws Exception {
from("direct:generic-route")
.process(Util.getProcesss(“${exchangeProperty[processKey]"))
.ToD(Util.getUrl(“${exchangeProperty[urlKey]"));
}
}
Public class Util{
Map<String,Object> routerResources;
static {
//load routerResources
}
public static Processor getProcessor(String processorKey){
return (Processor)routerResources.get(processorKey);
}
public static Processor getUrl(String urlKey){
return (String)routerResources.get(urlKey);
}
}
The generic router is expected to post a rest call. the properties "urlKey" and "processorUrl" are already available in exchange. I finding it difficult to pass exchange properties to static Util class methods.
If you want to access properties of an exchange in plain java you can use .process or .exchange. If you need to access body or headers you can use e.getMessage().getBody() and e.getMessage().getHeader()
from("direct:generic-route")
.process( e -> {
String processKey = e.getProperty("processKey", String.class);
Processor processor = Util.getProcessor(processKey);
processor.process(e);
})
.setProperty("targetURL").exchange( e -> {
String urlKey = e.getProperty("urlKey", String.class);
return Util.getUrl(urlKey);
})
.toD("${exchangeProperty.targetURL}");
Also make sure you fix the return type of this method:
public static Processor getUrl(String urlKey){
return (String)routerResources.get(urlKey);
}
As a side note, you can actually use map stored in body, header or property through simple language.
public class ExampleTest extends CamelTestSupport {
#Test
public void example(){
template.sendBodyAndHeader("direct:example", null, "urlKey", "urlA");
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
Map<String, String> urlMap = new HashMap<>();
urlMap.put("urlA", "direct:pointA");
urlMap.put("urlB", "direct:pointB");
from("direct:example")
.setProperty("urlMap").constant(urlMap)
.log("url: ${exchangeProperty.urlMap['${headers.urlKey}']}");
}
};
}
}

Can't read properties file on device farm :- src/test/resources/Properties/Android_Or.properties -error

Please let me know how to read properties file in device farm, since following code is not working. I have looked into various solutions but still device farm is not able to recognize properties file, although this code works fine locally
public abstract class AndroidCapabilities {
// protected static AppiumDriver<MobileElement> driver;
public static ExtentHtmlReporter reporter;
public static ExtentReports extent;
public static ExtentTest logger1;
// #Parameters("browser")
// #BeforeSuite
// public void setUp() throws MalformedURLException {
public static AndroidDriver<MobileElement> driver;
public abstract String getName();
#BeforeTest
public abstract void setUpPage();
#BeforeSuite
public void setUpAppium() throws MalformedURLException {
DesiredCapabilities capabilities = new DesiredCapabilities();
// capabilities.setCapability("device", "Android");
// capabilities.setCapability("platformName", "Android");
// capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, "Chrome");
final String URL_STRING = "http://127.0.0.1:4723/wd/hub";
URL url = new URL(URL_STRING);
// Use a empty DesiredCapabilities object
driver = new AndroidDriver<MobileElement>(url, capabilities);
// Use a higher value if your mobile elements take time to show up
driver.manage().timeouts().implicitlyWait(35, TimeUnit.SECONDS);
}
public static Properties properties;
static {
properties = new Properties();
FileInputStream fis;
InputStream input;
try {
// fis = new FileInputStream(System.getProperty("user.dir") +
// "//src//test//resources//Properties//Android_OR.properties");
fis = (FileInputStream) Thread.currentThread().getContextClassLoader()
.getResourceAsStream("//Properties//Android_OR.properties");
System.out.println(properties.getProperty("url"));
properties.load(fis);
} catch (IOException e) {
e.printStackTrace();
}
}
#BeforeTest
public void createReport() {
reporter = new ExtentHtmlReporter("./extent.html");
extent = new ExtentReports();
extent.attachReporter(reporter);
}
#AfterTest
public void flush() throws IOException {
extent.flush();
// reporter.setAppendExisting(true);
}
#AfterSuite
public void closeApplication() {
driver.quit();
Reporter.log("===Session End===", true);
}
}
Faced the same issue. Instead of using System.getProperty, try this:
// pass fileName as "Android_OR.properties"
public Properties loadProperty(String fileName) {
Properties prop = new Properties();
try (InputStream input = Base.class.getClassLoader().getResourceAsStream(filePath)) {
if (input == null) {
System.out.println("Sorry, unable to find config.properties");
return prop;
}
// load a properties file from class path, inside static method
prop.load(input);
} catch (IOException ex) {
ex.printStackTrace();
}
return prop;
}

Doing bean inject in camel test

I have a camel app which look something like below which has a route like below:-
from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders");
The entry point of the code look something like below:
public class OrderMainApp {
public static void main(String... args) throws Exception {
OrderMainApp orderMainApp = new OrderMainApp();
DefaultCamelContext camelContext = new DefaultCamelContext();
ProducerTemplate producer = camelContext.createProducerTemplate();
camelContext.setRegistry(orderMainApp.createRegistry(producer));
camelContext.addRoutes(new OrderRouteBuilder(producer));
camelContext.start();
}
protected JndiRegistry createRegistry(ProducerTemplate producer) throws Exception {
JndiRegistry jndi = new JndiRegistry();
OrderHelper orderHelper = new OrderHelper();
orderHelper.setProducer(producer);
jndi.bind("orderHelper", orderHelper);
return jndi;
}
}
In OrderRouteBuilder configure has routes like below:-
//processor is a custom JSONProcessor extending Processor
from("jetty:http://localhost:8888/orchestratorservice").process(processor);
from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders");
My goal is to test the response I receive from bean:orderHelper?method=getMarketplaceOrders when I place a request on direct:getMarketplaceOrders
orderHelper.getMarketplaceOrders looks like below:-
public OrderResponse getMarketplaceOrders(GetMarketplaceOrdersRequest requestParam) throws Exception
My test class look something like below:-
public class OrderMainAppTest extends CamelTestSupport {
#Produce(uri = "direct:getMarketplaceOrders")
protected ProducerTemplate template;
#EndpointInject(uri = "bean:orderHelper?method=getMarketplaceOrders")
protected MockEndpoint resultEndpoint;
#Test
public void testSendMatchingMessage() throws Exception {
String expectedBody = "<matched/>";
template.sendBody("{\"fromDateTime\": \"2016-01-11 10:12:13\"}");
resultEndpoint.expectedBodiesReceived(expectedBody);
resultEndpoint.assertIsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders");
}
};
}
}
Whenever I am running the test I am getting the below exception:-
java.lang.IllegalArgumentException: Invalid type: org.apache.camel.component.mock.MockEndpoint which cannot be injected via #EndpointInject/#Produce for: Endpoint[bean://orderHelper?method=getMarketplaceOrders]
I am guessing this is because I am not able to pass on OrderHelper to the camel test context. Can some one let me know how can I inject the bean in the mock result end point?
EDIT:-
I tried modifying my test class as follows:-
public class OrderMainAppTest extends CamelTestSupport {
protected OrderHelper orderHelper = new OrderHelper();
#Produce(uri = "direct:getMarketplaceOrders")
protected ProducerTemplate template;
#EndpointInject(uri = "mock:intercepted")
MockEndpoint mockEndpoint;
#Before
public void preSetup() throws Exception {
orderHelper.setProducer(template);
};
#Test
public void testSendMatchingMessage() throws Exception {
GetMarketplaceOrdersRequest request = new GetMarketplaceOrdersRequest();
request.setFromDateTime("2016-01-11 10:12:13");
request.setApikey("secret_key");
request.setMethod("getMarketplaceOrders");
request.setLimit(10);
request.setOffset(2);
template.sendBody(request);
mockEndpoint.expectedBodiesReceived("{\"success\":\"false\"");
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
interceptSendToEndpoint("bean:orderHelper?method=getMarketplaceOrders")
.to("mock:intercepted"); from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders");
}
};
}
#Override
protected JndiRegistry createRegistry() throws Exception {
return getRegistry();
}
protected JndiRegistry getRegistry() {
JndiRegistry jndi = new JndiRegistry();
jndi.bind("orderHelper", orderHelper);
return jndi;
}
}
The above code is making the request correctly and is flowing through my app correctly. But I am not able to intercept the response of orderHelper.getMarketplaceOrders. The above code is intercepting only the request. I tried changing to template.requestBody(request). But still no luck.
This error means you can't inject a bean: endpoint into a MockEndpoint.
If you want to "intercept" the call into your OrderHelper, you can use interceptSendToEndpoint in your route :
#EndpointInject(uri = "mock:intercepted")
MockEndpoint mockEndpoint;
...
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
interceptSendToEndpoint("bean:orderHelper?method=getMarketplaceOrders")
.to("mock:intercepted");
from("direct:getMarketplaceOrders")
.to("bean:orderHelper?method=getMarketplaceOrders");
}
};
See : http://camel.apache.org/intercept.html
By updating my createRouteBuilder as shown below. I am able to intercept the response and send it to a mock endpoint where I can do the assertion.
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() {
from("direct:getMarketplaceOrders").to("bean:orderHelper?method=getMarketplaceOrders").onCompletion()
.to("mock:intercepted");
}
};
}

Error Injecting endpoint into a bean in Camel

I have a bean defined with the annotation.I tried using CamelBeanPostProcessor but the camelContext is null.
public class HelloWorld {
#EndpointInject(uri="direct:copy")
private ProducerTemplate template;
public final void speak(Exchange e) {
template.sendBody("A new message");
}
public ProducerTemplate getTemplate() {
return template;
}
public void setTemplate(ProducerTemplate template) {
this.template = template;
}
}
There are quite a lot of ways of achieving this. As your bean is a processor, you can simply implement Processor and then have access to the entire exchange, and of course camelcontext as well:
public class HelloWorld implements Processor {
public void process(Exchange exchange) throws Exception {
context = exchange.getContext()
}
}

Resources