SyntaxHighlighter

Showing posts with label JUnit. Show all posts
Showing posts with label JUnit. Show all posts

Tuesday, August 20, 2013

Spring Context PropertyPlaceholder and ActiveProfiles

Spring Context PropertyPlaceholder and ActiveProfiles

so here's a little design requirement that i ran into; i want to use the < context:property-placeholder /> with different configurations in my application context dependent on the Active Profile.  that is, in "production" i want it to read from a properties file, but for testing, i want to set all the values via properties object defined in Spring.

normally, with ActiveProfiles you can declare a bean definition in the regular bean namespace then define it again (with whatever is required) in the beans (profile) tag.  this doesn't work with the < context:property-placeholder /> as it doesn't have an id attribute.  (there's a bunch more sophisticated/accurate/clever reasons as to why, but that's the most readily identifiable).  as such, the profile won't override it, but instead create a second one.  no good for the design.

so here's the solution; default profile.  essentially we create all our beans, then create a default profile and inside it, declare our production < context:property-placeholder />. to declare our test or alternative profile, we just do the same again.



 
  
 

 
  
 
 
 
  
  
  
   hello world
  
 





the default profile is the profile picked up by "default" when the application context is started.  as such, we don't need to specify it and it is akin to not wrapping anything in profiles.  it helps us in this case where we don't want a particular element to be referenced at all if we have a profile in play.

Saturday, August 17, 2013

How to Emulate a remote JVM in JUnit using Spring, Ant, H2 and ActiveMQ

How to Emulate a remote JVM in JUnit using Spring, Ant, H2 and ActiveMQ


recently i needed to test a scenario where one JVM was communicating to another using both a shared database and JMS.  i also needed to have each JVM be "invisible" to each other and make sure that the couldn't 'accidentally' share things across and application context.

so, to do this, i needed to kick start a separate JVM from inside a JUnit test, and communicate via JMS and a database... fun.

here's what i needed;

- shared database (standalone server)
- shared JMS (standalone server)
- JVM A - application context with unique configuration
- JVM B - application context with unique configuration

so, here's the solution i put together with some google/stackoverflow help and the following;
- Spring
- JUnit 4
- Ant
- H2
- ActiveMQ

here's the infrastructure utilities class.  it's responsible for setting up 'shared' infrastructure such as H2 and ActiveMQ.  it also provides hooks for binding to JNDI in a standalone context to help with sharing the resources in an 'agnostic' kind of way.

package de.incompleteco.spring.batch.ha;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.spring.ActiveMQConnectionFactory;
import org.h2.jdbcx.JdbcConnectionPool;
import org.h2.jdbcx.JdbcDataSource;
import org.h2.tools.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;

public class InfrastructureUtils {

 private static final Logger logger = LoggerFactory.getLogger(InfrastructureUtils.class);
 
 private static Server server;
 
 private static BrokerService broker;
 
 public static void startH2() throws Exception {
  if (server == null) {
   server = Server.createTcpServer("-tcpAllowOthers");
   server.start();
   logger.info(server.getStatus());
  }//end if
 } 
 
 public static void stopH2() throws Exception {
  if (server != null) {
   server.stop();
  }//end if
 } 
 
 public static void startAMQ() throws Exception {
  if (broker == null) {
   broker = new BrokerService();
   broker.addConnector("tcp://localhost:61616");
   broker.setUseJmx(false);
   broker.setUseShutdownHook(true);
   broker.start();
   broker.deleteAllMessages();//clean up
  }//end if
 }
 
 public static void stopAMQ() throws Exception {
  if (broker != null) {
   broker.stop();
  }//end if
 } 
 
 public static void bindLocalAMQ(String connectionFactoryName,String... queueNames) {
  //get jndi
  SimpleNamingContextBuilder builder = SimpleNamingContextBuilder.getCurrentContextBuilder();
  //get the connection
  ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
  connectionFactory.setBrokerURL("tcp://localhost:61616");
  //bind
  builder.bind("jms/" + connectionFactoryName,connectionFactory);
  //setup the queues
  for (String name : queueNames) {
   builder.bind("jms/" + name,new ActiveMQQueue(name));   
  }//end for
 } 
 
 public static DataSource bindLocalH2(String dataSourceName) throws Exception {
  JdbcDataSource dataSource = new JdbcDataSource();
  dataSource.setURL("jdbc:h2:tcp://localhost/~/test");
  //build a pool and bind
  JdbcConnectionPool pool = JdbcConnectionPool.create(dataSource);
  SimpleNamingContextBuilder builder = SimpleNamingContextBuilder.getCurrentContextBuilder();
  builder.bind("jdbc/" + dataSourceName, pool);
  //return it in case there's other uses
  return dataSource;
 } 
 
 public static String[] convertSqlFile(String location) throws Exception {
  List sqlStatements = new ArrayList();
  //load up the file
  BufferedReader reader = new BufferedReader(new InputStreamReader(TestSimpleBatchHAService.class.getResourceAsStream(location)));
  String line;
  StringBuilder statement = new StringBuilder();
  while ((line = reader.readLine()) != null) {
   if (line.contains(";") && !line.contains("--")) {
    statement.append(line);
    sqlStatements.add(statement.toString().replace(';',' '));
    statement = new StringBuilder();//reset the string
   } else if (line.contains("--")) {
    //ignore
   } else {
    statement.append(line);
   }//end if
  }//end while
  reader.close();
  //return
  return sqlStatements.toArray(new String[sqlStatements.size()]);
 } 
}


here's the RemoteJVMEmulator class.  it's a 'standalone' class (static void main(..)) that bootstraps and application context.  it also uses the infrastructure utilities to bind the resources to the JNDI.

package de.incompleteco.spring.batch.ha;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;

public class RemoteJVMEmulator {

 private static final Logger logger = LoggerFactory.getLogger(RemoteJVMEmulator.class);
 
 //bind shared resources to JNDI
 public static void setupJNDI() throws Exception {
  SimpleNamingContextBuilder.emptyActivatedContextBuilder();
  //bind AMQ
  InfrastructureUtils.bindLocalAMQ("ConnectionFactory","request.queue","reply.queue");
  //bind h2
  InfrastructureUtils.bindLocalH2("DataSource");
 }
 
 public static void main(String[] args) throws Exception {
  //setup jndi
  setupJNDI();
  //start the app context
  ApplicationContext context = new ClassPathXmlApplicationContext(args);
  //print a statement saying "it's up"
  logger.info(RemoteJVMEmulator.class.getSimpleName() + " is up " + context.getStartupDate());
 }

}


and here's the test runner.  this uses the infrastructure utilities to startup the shared services and bind them locally.  it then uses and inner class to build and run and Ant task that will kick off the RemoteJVMEmulator class.  a couple of things to note about it though;
- the Ant task is started in a separate thread - mainly because it blocks
- the Ant task is started first to act as the 'remote client'/'listener'
- the 'remote client'/'listener' needs to be setup such that you don't need to be in the same JVM to access any information from it.  (treat it as a 'remote' server deployed and running without any interaction services - you could connect to it via JMX or expose other services, that's up to you)

package de.incompleteco.spring.batch.ha;

import static org.junit.Assert.assertFalse;

import java.io.PrintStream;

import javax.sql.DataSource;

import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.DemuxOutputStream;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Java;
import org.apache.tools.ant.types.Path;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;

//don't run this test in CI
@Ignore
public class TestSimpleBatchHAService {

 
 @BeforeClass
 public static void beforeClass() throws Exception {
  //start a builder
  SimpleNamingContextBuilder.emptyActivatedContextBuilder();
  //setup the services (h2 and amq)
  InfrastructureUtils.startH2();
  //setup the database
  DataSource dataSource = InfrastructureUtils.bindLocalH2("DataSource");
  setupH2Data(dataSource);
  //start amq
  InfrastructureUtils.startAMQ();
  //bind
  InfrastructureUtils.bindLocalAMQ("ConnectionFactory","request.queue","reply.queue");
 }
 
 @AfterClass
 public static void afterClass() throws Exception {
  //shutdown the services
  InfrastructureUtils.stopH2();
  InfrastructureUtils.stopAMQ();
 }
 
 private static void setupH2Data(DataSource dataSource) throws Exception {
  //execute the statements
  JdbcTemplate template = new JdbcTemplate(dataSource);
  //drop statements
  String[] statements = InfrastructureUtils.convertSqlFile("/org/springframework/batch/core/schema-drop-h2.sql");
  try {
   for (String statement : statements) {
    template.execute(statement);
   }//end for
  }
  catch (Exception e) { }
  statements = InfrastructureUtils.convertSqlFile("/META-INF/sql/schema-ext-drop-h2.sql");
  try {
   for (String statement : statements) {
    template.execute(statement);
   }//end for
  }
  catch (Exception e) { }  
  //create statements
  statements = InfrastructureUtils.convertSqlFile("/org/springframework/batch/core/schema-h2.sql");
  for (String statement : statements) {
   template.execute(statement);
  }//end for  
  //create statements
  statements = InfrastructureUtils.convertSqlFile("/META-INF/sql/schema-ext-h2.sql");
  for (String statement : statements) {
   template.execute(statement);
  }//end for    
 }
 
 
 @Test
 public void testExecute() throws Exception {
  //start a thread to run the remote
  new Thread(new RemoteJVMRunner()).start();
  //now start the 'server'
  ApplicationContext context = new ClassPathXmlApplicationContext("classpath:/META-INF/spring/server-context.xml");
  //now that it's started, run the job
  Job job = context.getBean(Job.class);
  JobParameters parameters = new JobParametersBuilder().addLong("runtime",System.currentTimeMillis()).toJobParameters();
  JobLauncher launcher = context.getBean("remoteJobLauncher", JobLauncher.class);
  JobExecution execution = launcher.run(job,parameters);
  JobExplorer explorer = context.getBean(JobExplorer.class);
  //monitor
  while (explorer.getJobExecution(execution.getId()).isRunning()) {
   Thread.sleep(500);
  }//end while
  //reload the execution
  execution = explorer.getJobExecution(execution.getId());
  //check
  assertFalse(execution.getStatus().isUnsuccessful());
 }

 
 class RemoteJVMRunner implements Runnable {
  
  @Override
  public void run() {
   Project project = new Project();
   project.setName("remote-jvm");
   project.init();
   //setup the logger
   DefaultLogger logger = new DefaultLogger();
   project.addBuildListener(logger);
   logger.setOutputPrintStream(System.out);
   logger.setErrorPrintStream(System.err);
   logger.setMessageOutputLevel(Project.MSG_INFO);
   System.setOut(new PrintStream(new DemuxOutputStream(project,false)));
   System.setErr(new PrintStream(new DemuxOutputStream(project,true)));
   //start the project
   project.fireBuildStarted();
   
   Java java = new Java();
   java.setProject(project);;
   java.setTaskName("run-remote-jvm");
   java.setFork(true);
   java.setFailonerror(true);
   //set the classname
   java.setClassname(RemoteJVMEmulator.class.getName());
   java.setClasspath(new Path(project,System.getProperty("java.class.path")));
   //create arguments
   java.createArg().setValue("classpath:/META-INF/spring/client-context.xml");
   //init
   java.init();
   //execute
   java.executeJava();
  }
 }
}


Saturday, April 6, 2013

Spring Web MVC - without XML

Spring Web MVC - without XML


how to configure a Spring Web MVC app without any XML thanks to Servlet 3.0.

here's the controller;

package de.incompleteco.spring.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/simple")
public class SimpleController {

 @RequestMapping(method=RequestMethod.GET)
 public @ResponseBody String get() throws Exception {
  return "hello world";
 }
 
}


here's the @Configuration class

package de.incompleteco.spring.web;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@EnableWebMvc
@ComponentScan("de.incompleteco.spring.web")
public class WebAppConfig {
 
}

here's the Servlet 3.0 compliant bootstrap

package de.incompleteco.spring.web;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class Bootstrap implements WebApplicationInitializer {

 @Override
 public void onStartup(ServletContext servletContext) throws ServletException {
  AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();
  mvcContext.register(WebAppConfig.class);
  //register
  ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(mvcContext));
  dispatcher.setLoadOnStartup(1);
  dispatcher.addMapping("/app/*");
 }

}


and here's the JUnit test

package de.incompleteco.spring.web.controller;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;

import de.incompleteco.spring.web.WebAppConfig;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes={WebAppConfig.class})
public class SimpleControllerTest {

 @Resource
 private WebApplicationContext context;
 
 @Test
 public void testGet() throws Exception {
  //setup
  MockMvc mvc = webAppContextSetup(context).build();
  //execute
  mvc.perform(get("/simple")).andExpect(status().isOk()).andExpect(content().string("hello world"));
 }

}

and here's the sample up on github

https://github.com/incomplete-code/spring-web-config

Friday, March 29, 2013

Spring Integration, Spring JMS, ActiveMQ and testing Topics

Spring Integration, Spring JMS, ActiveMQ and testing Topics

so everyone knows how to test a JMS Queue in JUnit, ActiveMQ and - to make life easy - Spring JmsTemplate.  but, testing a Topic is a slightly different matter.

first, a topic subscriber has to be 'connected' before you can publish the message.  that's a bit of a challenge to setup.  but, with a little Spring Integration to the rescue we can get a subscriber sorted and sne a message via the JmsTemplate.

another catch comes from ActiveMQ.  to test queues, you can setup the ActiveMQConnectionFactory and you're done.  however, with Topics, you'll need the PooledConnectionFactory as well.

first, a couple dependencies for our pom


   
    org.apache.activemq
    activemq-core
    5.6.0
    test
   
   
    org.apache.activemq
    activemq-pool
    5.6.0
    test
   


then a little Spring Integration to listen to the Topic




 
  
 
  
 
 



(i prefer a queue channel, just to make it easier to get the messages off in testing)
so the next part would be the test class

package de.incompleteco.spring.integration.test;

import static org.junit.Assert.assertNotNull;

import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.Topic;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.core.PollableChannel;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/META-INF/spring/integration-context.xml"})
public class TopicIntegrationTest {

 @Autowired
 private PollableChannel receiveChannel;
 
 @Autowired
 private ConnectionFactory connectionFactory;
 
 @Autowired
 private Topic topic;
 
 private JmsTemplate jmsTemplate;
 
 @Test
 public void test() throws Exception {
  //setup the jmsTemplate and send
  jmsTemplate = new JmsTemplate(connectionFactory);
  jmsTemplate.setDefaultDestination(topic);
  jmsTemplate.setPubSubDomain(true);//it's a topic
  //send
  jmsTemplate.send(new MessageCreator() {

   public javax.jms.Message createMessage(Session session) throws JMSException {
    return session.createTextMessage("hello world");
   } });
  //now grab it off the queue
  Message message = receiveChannel.receive(1000);//let's put a timeout
  //test
  assertNotNull(message);
 }
 
}


and here's the specific apache configuration to support Topics



 
  
   
    
   
  
 





Thursday, March 28, 2013

Spring Integration HTTP - Testing

Spring Integration HTTP - testing

with the new Spring 3.2, there's an even easier way to test Spring MVC and, in turn, Spring Integration HTTP endpoints; MockMvc.

here's a sample Spring Integration HTTP endpoint that, when invoked, logs the passed parameter.  (really quick and dirty)





 
 
 
 
 
 
 
 





now to test this

package de.incompleteco.spring.integration;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({"classpath:/META-INF/spring/integration-context.xml"})
public class IntegrationContextTest {

 @Autowired
 private WebApplicationContext context;
 
 @Test
 public void test() throws Exception {
  //setup the mock mvc
  MockMvc mockMvc = webAppContextSetup(context).build();
  //execute
  mockMvc.perform(put("/log").content("something to log")).andExpect(status().isOk());
 }
 
}


Mockito Pointers

Mockito Pointers

here's a couple of pointers on Mockito that i don't want to lose;

mock something


   ServletContext servletContext = mock(ServletContext.class);


control the response


    when(servletContext.getContextPath()).thenReturn("some value");


when you don't know the argument value


  when(servletContext.getRealPath(anyObject()).thenReturn("something");

Monday, July 16, 2012

Groovy DDLUtils by Scott Frederick

Groovy DDLUtils by Scott Frederick

DDLUtils is a great little apache project that really can help standardize and minimize the impact of databases in projects from your in-memory database to production.  But, Scott Frederick has made it a little more accessible with his Groovy DDLUtils.

Check out his blog post here


http://scottfrederick.blogspot.com/2011/11/groovy-ddlutils.html


and the project here


https://github.com/scottfrederick/groovy-ddlutils


the base DDLUtils is here


http://db.apache.org/ddlutils/

Friday, July 13, 2012

MongoDB Embedded

Embedded MongoDB for Unit Tests



https://github.com/flapdoodle-oss/embedmongo.flapdoodle.de


and here's the maven



    de.flapdoodle.embedmongo
    de.flapdoodle.embedmongo
    1.16
and here's a sample of how to use it





and a better article on how to use it


http://www.cubeia.com/index.php/blog/archives/436

Wednesday, July 11, 2012

Spring Integration - Testing Strategies (Part 1)

Separation of Adapters from Channels

one of the interesting little testing techniques i've encountered with Spring Integration is to create separate xml files for your channel logic and your adapters.  this helps in two ways;
- clearly separates your 'logic' from your communications channels (e.g. JMS from Routers)
- can assist with unit testing by attacking separation of concerns

so here's the 'logic' part of spring integration



and here's the 'communications channels' (a simple http inbound gateway)



this will allow us to JUnit test just the logic without testing the http gateway