I am using Apache Camel + SpringBoot + JDBC.I would like to do JUnit testing for end to end camel routes.My Processor is having DB Calls with Logic.But I am struggling to remove DB calls mocking to achieve testing.My junit is working but I dont know how to mock the JDBC object inside of my processor.I have lots of boiler plate code in my processors so I don't want to duplicate my logic inside of junits as I have done in this junit example.Please help me to solve this.
Applicaiton.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
MyRouter.java
#Component
public class MyRouter extends RouteBuilder {
#Autowired
OrderProcessor processor;
#Override
public void configure() throws Exception {
from("file://src/main/resources/input?delete=true&moveFailed=.error").routeId("route1").convertBodyTo(byte[].class)
.process(processor).split(simple("${body}")).to("file://src/main/resources/output?fileName=outputfile.txt&fileExist=Append");
}
}
OrderProcessor.java
#Component
public class OrderProcessor implements Processor{
#Autowired
DataSource dataSource;
#Autowired
OrderRepository repository;
#Override
public void process(Exchange exchange) throws Exception {
System.out.println("process starts...");
byte[] data = exchange.getIn().getBody(byte[].class);
File dataFile = File.createTempFile("filepre", "bin");
FileUtils.writeByteArrayToFile(dataFile, data);
List<String> orders = new ArrayList<>();
FileReader fileReader = new FileReader(dataFile);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
line = line.substring(line.length()-1);
orders.add(repository.getOrder(line).toString()+"\n");
}
fileReader.close();
exchange.getIn().setBody(orders);
}
}
Order.java
public class Order {
private int id;
private int amount;
// getters and setters
#Override
public String toString() {
return this.id + ":" + this.amount;
}
}
OrderRepository.java
#Repository
public class OrderRepository {
#Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
public Order getOrder(String id) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("refid", id);
Order extObj = jdbcTemplate.query("select * from world.orders where id = :refid",parameters, rs -> {
Order innerObj = null;
if (rs != null) {
while (rs.next()) {
innerObj = new Order();
innerObj.setId(rs.getInt("id"));
innerObj.setAmount(rs.getInt("amount"));
}
}
return innerObj;
});
return extObj;
}
}
jUnit Class:
public class MyRouterTest extends CamelSpringTestSupport {
#Override
protected AbstractApplicationContext createApplicationContext() {
return new AnnotationConfigApplicationContext();
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("file://src/test/resources/input?delete=true&moveFailed=.error").routeId("route1")
.convertBodyTo(byte[].class).process(exchange -> {
Map<String, Integer> mockValues = new HashMap<>();
mockValues.put("1", 8);
mockValues.put("2", 10);
mockValues.put("3", 5);
System.out.println("process starts...");
byte[] data = exchange.getIn().getBody(byte[].class);
File dataFile = File.createTempFile("filepre", "bin");
FileUtils.writeByteArrayToFile(dataFile, data);
List<String> orders = new ArrayList<>();
FileReader fileReader = new FileReader(dataFile);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
line = line.substring(line.length() - 1);
Order orderTmp = new Order();
orderTmp.setId(Integer.parseInt(line.substring(line.length() - 1)));
orderTmp.setAmount(mockValues.get(line.substring(line.length() - 1)).intValue());
orders.add(orderTmp.toString() + "\n");
}
fileReader.close();
exchange.getIn().setBody(orders);
}).split(simple("${body}"))
.to("file://src/test/resources/output?fileName=outputfile.txt&fileExist=Append");
}
};
}
#Test
public void checkFileExistsInOutputDirectory() throws InterruptedException {
Thread.sleep(15000);
File file = new File("src/test/resources");
assertTrue(file.isDirectory());
// Check file content
}
}
inputfile.txt
line1
line2
line3
outputfile.txt
1:8
2:10
3:5
I think that you are trying to test too much, processor logic is not in the scope of MyRouter class, so you should use a mock. Tests for processor should be done separately.
My advice:
create mock of OrderProcessor
inject mock of OrderProcessor to real implementation of MyRouter
pass MyRouter instance (with mocked processor inside) to createRouteBuilder method in test
call route in tests
With Mockito it should be quite easy:
public class MyRouterTest extends CamelTestSupport {
#Mock
private OrderProcessor orderProcessor;
#InjectMocks
private MyRouter myRouter;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks( this );
super.setUp(); //important
doAnswer( invocation -> {
Exchange exchange = invocation.getArgumentAt( 0, Exchange.class );
//mock processor logic;
return null;
} ).when( orderProcessor ).process( any() );
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return myRouter;
}
#Test
public void testYourStuff() {
}
}
Related
I have completed a spring batch (Standalone Jar) in Spring Boot + CommandLineRunner. But the JVM is not getting shutdown after completion. After doing some research initially I thought its not shutting down because of below reasons.
1 . I am not closing the spring application context at the end ofcommandline runner.
2 . Executor service is not shutdown properly which might have caused the JVM from shutting down.
I dont want to call system.exit which is a forceful shutdown.
I tried closing the application context and also verified executor service is shutdown using isShutdown method (returns true).
Then I found out the root cause, it is because I am calling a static method which is the culprit. When I commented the static method call, the job was shutting down gracefully even if I dont close the application context explicitly.
I am not sure why this behavior and do I need to convert everything to objects or is there something else I am missing here. Can someone please throw some light.
Main Class
#SpringBootApplication
#ComponentScan(basePackages = "com.acn.abp.printbatch")
#EnableTransactionManagement
#ImportResource({ "ABPBatchInfrastructure.xml", "financeBillPayAppConfig.xml" })
public class financeBillPayFileUploadApplication extends PrintBatchConstants implements CommandLineRunner {
#Autowired
private NotifyConfig notify;
#Autowired
private ApplicationContext ctx;
static final Logger logger = LoggerFactory.getLogger(financeBillPayFileUploadApplication.class);
public static void main(String[] args) {
SpringApplication application = new SpringApplication(financeBillPayFileUploadApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
#Override
public void run(String... args) throws Exception {
logger.info(notify.getEnvironment());
JobLauncher jobLauncher = ctx.getBean(JobLauncher.class);
Job job = ctx.getBean(Job.class);
jobLauncher.run(job,
new JobParametersBuilder()
.addString(batchDocumentClass, "InvoiceStatementDocumentation")
.addString(batchType, "2020-06-04")
.addString(batchEmailID, notify.getSupportEmailId())
.addString(batchEnvironment, notify.getEnvironment())
.toJobParameters());
System.out.println("Here cxf");
((ConfigurableApplicationContext)ctx).close();
}
}
Below Class which is causing the problem. If I comment out below code then everything works perfectly.
populateItemDocuments(job, printConfig.geteCMObjectStore(), printConfig.geteCMUserId());
Class file where this method is called
#Component
public class DuplexWorker implements Callable {
static final Logger logger = LoggerFactory.getLogger(DuplexWorker.class);
#Autowired
private ManageFormService formgmtClient;
#Autowired
private PostScriptService postScriptService;
#Autowired
private BarcodeService barcodeService;
private static CfBatchPrintConfiguration printConfig;
private static CfPersistenceUtil dbUtilService;
private static FilenetDocumentRetrieval docmgmtClient;
#Autowired
public DuplexWorker(CfPersistenceUtil dbUtilService,CfBatchPrintConfiguration printConfig,FilenetDocumentRetrieval docmgmtClient) {
DuplexWorker.dbUtilService = dbUtilService;
DuplexWorker.printConfig = printConfig;
DuplexWorker.docmgmtClient=docmgmtClient;
}
private MailUtil mailUtil;
private NotifyConfig notify;
private List<PrintJobItem> printJobItems;
private List<String> groupIds;
private ArrayList duplexJobs;
private String groupId;
private CountDownLatch latch;
public DuplexWorker(ArrayList duplexJobs, String groupId,CountDownLatch latch) {
super();
this.latch=latch;
this.duplexJobs = duplexJobs;
this.groupId = groupId;
}
public DuplexWorker(CountDownLatch latch, MailUtil mailUtil,NotifyConfig notify,List<PrintJobItem> findByPrintStatusEquals,List<String>groupIds) {
this.latch=latch;
this.mailUtil=mailUtil;
this.notify=notify;
this.printJobItems=findByPrintStatusEquals;
this.groupIds=groupIds;
}
#Override
public Object call() throws Exception {
try {
if ((duplexJobs != null) && (!duplexJobs.isEmpty())) {
String prevJobId = null;
int docCount = 0;
CvPrintJob consolidatedPrintJob = (CvPrintJob)duplexJobs.get(0);
ArrayList printItems = new ArrayList();
if (consolidatedPrintJob != null)
{
ArrayList items = consolidatedPrintJob.getPrintJobItems();
int numPages = 0;
if ((items != null) && (!items.isEmpty()))
{
CvPrintJobItem firstItem = (CvPrintJobItem)items.get(0);
numPages = CfBatchPrintUtil.getItemTotalPages(firstItem);
logger.info("Item Total Pages == " + numPages);
logger.info("Job Item Page Limit == " +
printConfig.getJobItemPageLimit());
consolidatedPrintJob.setSequence(firstItem.getSequence());
}
if (numPages <= printConfig.getJobItemPageLimit())
{
consolidatedPrintJob.setHasLargeItems(false);
logger.info("Item setHasLargeItems == false");
}
else
{
consolidatedPrintJob.setHasLargeItems(true);
logger.info("Item setHasLargeItems == true");
}
}
ArrayList startBannerDataList = new ArrayList();
ArrayList barcodeList = new ArrayList();
ArrayList barcodeCorresPageCount = new ArrayList();
ArrayList statementNumberList = new ArrayList();
for (int i = 0; i < duplexJobs.size(); i++)
{
CvPrintJob job = (CvPrintJob)duplexJobs.get(i);
if ((prevJobId == null) ||
(!prevJobId.equalsIgnoreCase(job.getJobId()))) {
docCount = 0;
}
populateItemDocuments(job, printConfig.geteCMObjectStore(), printConfig.geteCMUserId());
}
consolidatedPrintJob.setPrintJobItems(printItems);
}
else
{
logger.info("====================================================================");
logger.info("=================>> No DUPLEX jobs to process <<===================");
logger.info("====================================================================");
}
duplexJobs = null;
this.latch.countDown();
System.gc();
return null;
}catch(Exception e) {
e.printStackTrace();
return null;
}
}
public static void populateItemDocuments(CvPrintJob job, String objectStore, String userid)
throws CfException
{
logger.info("Enters populateItemDocuments");
try
{
ArrayList items = job.getPrintJobItems();
job.setIsProcess(true);
ArrayList modelDocList = null;
logger.info("Items size::::::" + items.size());
for (int i = 0; i < items.size(); i++)
{
modelDocList = new ArrayList();
CvPrintJobItem x = (CvPrintJobItem)items.get(i);
ArrayList guidList = x.getGuidList();
if ((guidList != null) && (!guidList.isEmpty())) {
modelDocList.addAll(guidList);
}
logger.info("guidList size::::::" + guidList.size());
CvRenderPayloadRequest cvRenderPayloadRequest = null;
if ((modelDocList != null) && (!modelDocList.isEmpty()))
{
cvRenderPayloadRequest = new CvRenderPayloadRequest();
logger.info("Before creating CvRenderPayloadRequest");
logger.info("Document Class::: " +
x.getDocumentClass());
cvRenderPayloadRequest.setDocumentClass(
x.getDocumentClass());
cvRenderPayloadRequest.setGuid(modelDocList);
cvRenderPayloadRequest.setUserId(userid);
logger.info("After creating the CvRenderPayloadRequest");
try
{
if (cvRenderPayloadRequest != null)
{
List pdfContents = docmgmtClient.retrieveDocument(cvRenderPayloadRequest.getGuid());
if ((pdfContents != null) &&
(!pdfContents.isEmpty()))
{
logger.info(
"PDF contents sizenew::::::::::::::" + pdfContents.size());
Iterator pdfItr = pdfContents.iterator();
while (pdfItr.hasNext())
{
byte[] contents = (byte[])pdfItr.next();
CvPrintJobItem item = (CvPrintJobItem)items.get(i);
item.addDocumentList(contents);
int filenetpagecount = 100;
item.setPageCountFromFileNet(filenetpagecount);
logger.info("PageCOunt from Filenet " + filenetpagecount);
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
throw new CfException(" Error populating documents" + e);
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
throw new CfException(" Error populating documents" + e);
}
logger.info("Exits populateItemDocuments");
}
First of all you are using Tomcat server that runs the application. If you want to make standalone spring application you can configure like below
#Configuration
public class ApplicationMain {
#Bean
public Stackoverflow stackoverflow() {
return new Stackoverflow ();
}
public static void main(String[] args) {
ConfigurableApplicationContext configurableApplicationContext = new AnnotationConfigApplicationContext(ApplicationMain.class);
System.out.println(configurableApplicationContext.getBean("stackoverflow"));
}
}
'JVM is not getting shutdown after completion.' is normal behavior for Tomcat server because it waits for request to handle.
You can give basepackage like below
new AnnotationConfigApplicationContext("com.example");
it will scan the package for you
Can I somehow use doAnswer() when an exception is thrown?
I'm using this in my integration test to get method invocations and the test in configured the #RabbitListenerTest...
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyIT {
#Autowired
private RabbitTemplate rabbitTemplate;
#Autowired
private MyRabbitListener myRabbitListener;
#Autowired
private RabbitListenerTestHarness harness;
#Test
public void testListener() throws InterruptedException {
MyRabbitListener myRabbitListener = this.harness.getSpy("event");
assertNotNull(myRabbitListener);
final String message = "Test Message";
LatchCountDownAndCallRealMethodAnswer answer = new LatchCountDownAndCallRealMethodAnswer(1);
doAnswer(answer).when(myRabbitListener).event(message);
rabbitTemplate.convertAndSend("exchange", "key", message);
assertTrue(answer.getLatch().await(20, TimeUnit.SECONDS));
verify(myRabbitListener).messageReceiver(message);
}
#Configuration
#RabbitListenerTest
public static class Config {
#Bean
public MyRabbitListener myRabbitListener(){
return new MyRabbitListener();
}
}
}
It works ok but when I introduce an Exception being thrown, It doesn't i.e
This works
#RabbitListener(id = "event", queues = "queue-name")
public void event(String message) {
log.info("received message > " + message);
}
This doesn't
#RabbitListener(id = "event", queues = "queue-name")
public void event(String message) {
log.info("received message > " + message);
throw new ImmediateAcknowledgeAmqpException("Invalid message, " + message);
}
Any help appreciated
The LatchCountDownAndCallRealMethodAnswer is very basic
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
invocation.callRealMethod();
this.latch.countDown();
return null;
}
You can copy it to a new class and change it to something like
private volatile Exception exeption;
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
try {
invocation.callRealMethod();
}
catch (RuntimeException e) {
this.exception = e;
throw e;
}
finally {
this.latch.countDown();
}
return null;
}
public Exception getException() {
return this.exception;
}
then
assertTrue(answer.getLatch().await(20, TimeUnit.SECONDS));
assertThat(answer.getException(), isInstanceOf(ImmediateAcknowledgeAmqpException.class));
Please open a github issue; the framework should support this out-of-the-box.
I am new to Micronaut. I am trying to port a project to Micronaut (v1.1.1) and I have found a problem with Redis.
I am just trying to save a simple POJO in Redis, but when I try to "save" it the following error is raised:
io.lettuce.core.RedisException: io.netty.handler.codec.EncoderException: Cannot encode command. Please close the connection as the connection state may be out of sync.
Code is very simple (HERE you can find a complete test.):
class DummyTest {
#Test
public void testIssue() throws Exception {
final Date now = Date.from(Instant.now());
CatalogContent expectedContentOne = CatalogContent.builder()
.contentId(1)
.status(ContentStatus.AVAILABLE)
.title("uno")
.streamId(1)
.available(now)
.tags(Set.of("tag1", "tag2"))
.build();
repository.save(expectedContentOne);
}
}
/.../
class CatalogContentRepository {
private StatefulRedisConnection<String, CatalogContent> connection;
public CatalogContentRepository(StatefulRedisConnection<String, CatalogContent> connection) {
this.connection = connection;
}
public void save(CatalogContent content) {
RedisCommands<String, CatalogContent> redisApi = connection.sync();
redisApi.set(String.valueOf(content.getContentId()),content); //Error here!
}
}
Any idea will be welcomed.
Thanks in advance.
For the record I will answer my own question:
Right now (20190514) Micronaut only generate StatefulRedisConnection<String,String> with a hardcoded UTF8 String codec.
To change this you have to replace the DefaultRedisClientFactory and define a method returning the StatefulRedisConnection you need,
with your prefered codec.
In my case:
#Requires(beans = DefaultRedisConfiguration.class)
#Singleton
#Factory
#Replaces(factory = DefaultRedisClientFactory.class)
public class RedisClientFactory extends AbstractRedisClientFactory {
#Bean(preDestroy = "shutdown")
#Singleton
#Primary
#Override
public RedisClient redisClient(#Primary AbstractRedisConfiguration config) {
return super.redisClient(config);
}
#Bean(preDestroy = "close")
#Singleton
#Primary
public StatefulRedisConnection<String, Object> myRedisConnection(#Primary RedisClient redisClient) {
return redisClient.connect(new SerializedObjectCodec());
}
#Bean(preDestroy = "close")
#Singleton
#Primary
#Override
public StatefulRedisConnection<String, String> redisConnection(#Primary RedisClient redisClient) {
throw new RuntimeException("puta mierda");
}
#Override
#Bean(preDestroy = "close")
#Singleton
public StatefulRedisPubSubConnection<String, String> redisPubSubConnection(#Primary RedisClient redisClient) {
return super.redisPubSubConnection(redisClient);
}
}
Codec has been taken from Redis Lettuce wiki
public class SerializedObjectCodec implements RedisCodec<String, Object> {
private Charset charset = Charset.forName("UTF-8");
#Override
public String decodeKey(ByteBuffer bytes) {
return charset.decode(bytes).toString();
}
#Override
public Object decodeValue(ByteBuffer bytes) {
try {
byte[] array = new byte[bytes.remaining()];
bytes.get(array);
ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream(array));
return is.readObject();
} catch (Exception e) {
return null;
}
}
#Override
public ByteBuffer encodeKey(String key) {
return charset.encode(key);
}
#Override
public ByteBuffer encodeValue(Object value) {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bytes);
os.writeObject(value);
return ByteBuffer.wrap(bytes.toByteArray());
} catch (IOException e) {
return null;
}
}
}
Can anyone suggest me how to write JUnit for the below class:
#Controller
#RequestMapping(value = "/cutdata", consumes = "TEXT/XML")
public class CustController
{
Logger LOG = Logger.getLogger(CustController.class);
#Autowired
CustService custService;
#Autowired
MarCusService marCustService;
#Resource(name = "CustValidator")
private CusValidator validator;
#Resource(name = "cmsSiteService")
private CMSSiteService cmsSiteService;
protected CMSSiteService getCmsSiteService()
{
return cmsSiteService;
}
#RequestMapping(value = "/methodcall", method = RequestMethod.PUT)
public #ResponseBody ResponseEntity<?> methodCall(#RequestBody final CustDTO data)
throws WebServicesException
{
String statusCode = null;
try {
if (data.getGroup() != null && !data.getGroup().equals(String.valueOf(GroupEnum.ALL))) {
validator.validate(data);
}
} catch (WebServicesException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.OK);
}
try
{
final CMSSiteModel site = cmsSiteService.getCurrentSite();
String currentSiteId=site.getUid() !=null ? site.getUid():"";
if(StringUtils.contains(currentSiteId,Config.getParameter("****.siteuid")))
{
statusCode = marCustService.processData(data);
}
else
{
statusCode = custService.processData1(data);
}
final String[] message = statusCode.split(":");
final String code = message[0];
final String statusMessage = message[1];
if (code.equalsIgnoreCase("200"))
{
return new ResponseEntity<>(statusMessage, HttpStatus.CREATED);
}
else if (code.equalsIgnoreCase("400"))
{
return new ResponseEntity<>(statusMessage, HttpStatus.BAD_REQUEST);
}
}
catch (final Exception e)
{
LOG.error("log ::" + e);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
I'm new in writing JUnit Test case, i need help like how to write or how to start JUnit.
Basically, you need to make use of the Spring context to test Controller classes.
One example would be something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class CsrfShowcaseTests {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
public void shouldTestMethodCall() {
mockMvc.perform(put("/methodcall"))
.andExpect(status.isOk());
}
}
From this test you can expand the testing to whatever your flows are.
If you need more references, you can check Spring's documentation here.
-> controller.java
public controller() {
public controller(DataInterpreter interpret,ControllerClientUtility util, InterfaceConnection inter) {
interpreter = interpret;
utility = util;
interfaced = inter;
}
}
...
public void closeOne(String vpnSessionId) throws Exception {
try{
if ( interfaced.connect() && (interfaced.CheckIntegrity(SessionId)) ){
interfaced.kill(vpnSessionId);
}else{
closeAll();
}
}catch(Exception e){
if ( e.getMessage().startsWith("INTERFACE_ERR:") ){
closeAll();
}else{
throw new Exception(e);
}
}
}
-> methods in InterfaceConnection.java
public String getReponseFor(String command) throws Exception{
if (send(command)){
return receive();
}
else{
throw new Exception("INTERFACE_ERR: Could not get Response");
}
}
public List<String> getListOfConnections() throws Exception{
String statusResponse = getReponseFor("something");
..(regex searches and then make a list connectionsConnected)
return connectionsConnected;
}
public boolean CheckIntegrity(String SessionId){
try {
List<String> connections = new ArrayList<String>();
connections = getListOfConnections();
if (connections.contains(SessionId)){
return true;
}
return false;
}catch(Exception e){
return false;
}
}
Is there a way to mock the output of getListOfConnections ? I tried doing something like this but did not work
-> controllerTest.java
#Mock private InterfaceConnection interfaced;
#Before
public void beforeTests() throws Exception {
MockitoAnnotations.initMocks(this);
impl = new Controller(interpreter,utility,interfaced);
...
#Test
public void testDisconnectOneSessionWithBadSessionId_sendCommand() throws Exception{
String badSessionId = "123:123";
List<String> mockConnections = new ArrayList<String>();
mockConnections.add("asdasds");
when(interfaced.getListOfConnections()).thenReturn(mockConnections);
impl.closeOne(badSessionId);
Mockito.verify(utility)....
}
I hope I'm clear, thanks in advance.