Camel exchange header lost during test - apache-camel

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")

Related

apache camel JUnit5 test case for database operation

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

How to call POST api with content type x-www-urlencoded using camel

I have tried by writting the processor as follows
package com.example.demo;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.Processor;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TokenProcessor implements Processor {
#Override
public void process(Exchange exchange) throws Exception {
// TODO Auto-generated method stub
exchange.
System.out.println(exchange.getIn().getBody());
Message out = exchange.getOut();
Map<String,Object> map=new HashMap<String,Object>();
map.put(Exchange.HTTP_METHOD,"POST" );
map.put(Exchange.CONTENT_TYPE,"application/x-www-form-urlencoded" );
TokenInput tokeninput=exchange.getIn().getBody(TokenInput.class);
System.out.println("Tokeninput="+tokeninput);
// map.put("client_id",tokeninput.getClient_id());
// map.put("scope",tokeninput.getScope());
// map.put("code",tokeninput.getCode());
// map.put("redirect_uri",tokeninput.getRedirect_uri());
// map.put("grant_type",tokeninput.getGrant_type());
// map.put("client_secret",tokeninput.getClient_secret());
ObjectMapper obj=new ObjectMapper();
String jsonStr=obj.writeValueAsString(tokeninput);
//InputStream convertValue = obj.convertValue(tokeninput, Byte.class);
out.setHeaders(map);
//String encode = URLEncoder.encode(tokeninput.toString(),"utf-8");
out.setBody(exchange.getIn().getBody());
}
}
Gettring error
org.apache.camel.NoTypeConversionAvailableException: No type converter
available to convert from type: com.example.demo.TokenInput to the
required type: java.io.InputStream with value
com.example.demo.TokenInput#ea2c54c at
org.apache.camel.impl.converter.BaseTypeConverterRegistry.mandatoryConvertTo(BaseTypeConverterRegistry.java:181)
~[camel-core-2.12.0.jar:2.12.0] at
org.apache.camel.impl.MessageSupport.getMandatoryBody(MessageSupport.java:99)
~[camel-core-2.12.0.jar:2.12.0] at
org.apache.camel.processor.ConvertBodyProcessor.process(ConvertBodyProcessor.java:78)
~[camel-core-2.12.0.jar:2.12.0]
I use it extensively in camel applications as follows.
.removeHeaders("*")
.setHeader(Exchange.HTTP_METHOD, constant("POST"))
.setHeader(Exchange.CONTENT_TYPE, constant("application/x-www-form-urlencoded"))
.setBody(simple("grant_type=refresh&client_id=1233232&client_secret=mysecret"))
.to("http://google.com");

How to receive Snmp V3 traps in Camel SNMP?

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.

Null Pointer Exception using TestNG

I am trying to automate and have integrated Selenium with Appium and I am using Eclipse with TestNG for execution.
Now within a project I have a package with two classes; the first class is for login into the App and the second class is for logout
First class code -
package com.gma.test;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class LoginApp {
public AppiumDriver driver;
#BeforeSuite
public void beforeMethod() throws InterruptedException {
File app = new File("C:\\Users\\mc30058\\Downloads\\gma-QA-RELEASE-QRCode-07022015_v4.5.4028.apk");
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("deviceName","Android Emulator");
capabilities.setCapability("platformVersion", "4.4");
capabilities.setCapability("app", app.getAbsolutePath());
try {
driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
} catch (MalformedURLException e) {
e.printStackTrace();
}
driver.manage().timeouts().implicitlyWait(100, TimeUnit.SECONDS);
driver.findElement(By.xpath("//android.widget.Button[#resource-id='com.mcdonalds.app:id/button_choose_closest']")).click();
driver.findElement(By.xpath("//android.widget.Button[#text='Continue']")).click();
driver.findElement(By.xpath("//android.widget.Button[#text='OK']")).click();
}
#Test (priority=1)
public void Login() {
driver.manage().timeouts().implicitlyWait(100, TimeUnit.SECONDS);
driver.findElement(By.xpath("//android.widget.TextView[#text='Sign In']")).click();
driver.findElement(By.xpath("//android.widget.EditText[#resource-id='com.mcdonalds.app:id/signin_edittext_email']")).sendKeys("anuj.shrivastava#us.mcd.com");
driver.findElement(By.xpath("//android.widget.EditText[#resource-id='com.mcdonalds.app:id/signin_edittext_password']")).sendKeys("Anujtest2");
currentTime();
driver.findElement(By.xpath("//android.widget.Button[#text='Sign In']")).click();
driver.manage().timeouts().implicitlyWait(100, TimeUnit.SECONDS);
driver.findElement(By.xpath("//android.widget.Button[#text='Yeah, count me in!']")).isDisplayed();
currentTime();
driver.findElement(By.xpath("//android.widget.Button[#text='No, thanks. I like to be out of the loop']")).click();
}
private void currentTime() {
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
System.out.println( sdf.format(cal.getTime()) );
}
}
Second class code:
package com.gma.test;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class Logout {
public AppiumDriver driver;
#Test (priority=3)
public void LogoutApp() {
driver.findElement(By.xpath("//android.widget.TextView[#content-desc='Navigation Button']")).click();
driver.findElement(By.xpath("//android.widget.TextView[#text='Sign Out']")).click();
driver.findElement(By.xpath("//android.widget.Button[#text='Sign Out']")).click();
}
}
Contents from Testng.xml
<suite name = "GMA Automation">
<test name = "GMA Automation">
<classes>
<class name = "com.gma.test.LoginApp"/>
<class name = "com.gma.test.Logout"/>
</classes>
</test>
</suite>
I receive Null Pointer exception after execution of first class. Appium server stops saying no more commands received. Please help.
You are getting NPE b'coz you have declared one more driver reference public AppiumDriver driver; in your Logout class.
And you are using this driver reference without instantiation.
In logout class you forget to instantiate your AppiumDriver driver.
try this:
public AppiumDriver driver;
driver = new AndroidDriver();

Selenium Web driver--Failure Screenshot is not captured in TestNG report

With below mentioned code,if the test case is pass-screenshot captured successfully and displayed in report.But when the test is failed--screenshot is not displayed.Even screenshot hyperlink is not displayed in report.Anybody can sort out the mistake in code?
package listeners;
import java.io.File;
import java.io.IOException;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.TestListenerAdapter;
import java.util.logging.Logger;
#Listeners
public class CountryChoserLayer extends TestListenerAdapter {
#Test(priority=1)
public void choseCountry() throws Exception{
driver.findElement(By.id("intselect")).sendKeys("India");
driver.findElement(By.xpath(".//*[#id='countryChooser']/a/img")).click();
//window.onbeforeunload = null;
Date date=new Date();
Format formatter = new SimpleDateFormat("yyyy-MM-dd_hh-mm-ss");
File scrnsht = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
String NewFileNamePath=("C://Documents and Settings//vlakshm//workspace//MyTNG//test-output//Screenshots"+"//SearsINTL_"+ formatter.format(date)+".png");
FileUtils.copyFile(scrnsht, new File(NewFileNamePath));
System.out.println(NewFileNamePath);
Reporter.log("Passed Screenshot");
System.out.println("---------------------------------------");
System.out.println("Country choser layer test case-Success");
System.out.println("---------------------------------------");
}
public String baseurl="http://www.sears.com/shc/s/CountryChooserView?storeId=10153&catalogId=12605";
public WebDriver driver;
public int Count = 0;
#Test(priority=0)
public void openBrowser() {
driver = new FirefoxDriver();
driver.manage().deleteAllCookies();
driver.get(baseurl);
}
#Test(priority=2)
public void closeBrowser() {
driver.quit();
}
#Override
public void onTestFailure(ITestResult result){
Reporter.log("Fail");
System.out.println("BBB");
//Reporter.setCurrentTestResult(result);
Date date=new Date();
Format formatter = new SimpleDateFormat("yyyy-MM-dd_hh-mm-ss");
File scrnsht = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
//File scrFile = ((TakesScreenshot) WebDriver.globalDriverInstance).getScreenshotAs(OutputType.FILE);
String NewFileNamePath=("C://Documents and Settings//vlakshm//workspace//MyTNG//test-output//Screenshots"+"//SearsINTL_"+ formatter.format(date)+".png");
//System.out.println("AAA" + NewFileNamePath);
try {
//System.out.println("CCC");
FileUtils.copyFile(scrnsht,new File(NewFileNamePath));
System.out.println(NewFileNamePath);
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("DDD");
e.printStackTrace();
}
Reporter.log("Failed Screenshot");
Reporter.setCurrentTestResult(null);
System.out.println("---------------------------------------");
System.out.println("Country choser layer test case Failed");
System.out.println("---------------------------------------");
}
#Override
public void onTestSkipped(ITestResult result) {
// will be called after test will be skipped
Reporter.log("Skip");
}
#Override
public void onTestSuccess(ITestResult result) {
// will be called after test will pass
Reporter.log("Pass");
}
}
Your onTestFailure method is not being called because you didn't specify listener for your test class. You are missing a value in #Listeners annotation. It should be something like
#Listeners({CountryChoserLayer.class})
You can find more ways of specifying a listener in official TestNg's documentation.
Another problem you are likely to encounter would be NullPointerException while trying to take screenshot in onTestFailure method. The easiest workaround for that would be changing the declaration of driver field to static. I run the code with those fixes and I got the report with screenshot.
I must add that in my opinion putting both test and listener methods into one class is not a good practice.

Resources