Unit Testing Dilemma: Using a JNDI data source without running JBoss or Spring

Laura picture Laura · Oct 24, 2012 · Viewed 10.9k times · Source

Problem Statement

I want to be able to run junit tests on methods that connect to a database.

Current setup

Eclipse Java EE IDE – Java code is using no framework. The developers (me included) want more robust testing of current legacy code BEFORE attempting to move the code into a Spring framework so that we can prove along the way that the behavior is still correct.

JBoss 4.2 – Version limitation by vendor software (Adobe LiveCycle ES2); Our Java web application uses this setup of JBoss to run and makes use of the Adobe LiveCycle API.

We have been unable to successfully run the vendor configured JBoss within Eclipse – we have spent weeks attempting this, including contacting the company that provides our support for the configuration of JBoss for Adobe LiveCycle. Supposedly the problem is a memory limitation issue with settings in Eclipse, but changing the memory settings has thus far failed in a successful JBoss server start within Eclipse. For now, attempting to get JBoss to run inside of Eclipse is on hold.

The database connection is defined in a JNDI data source that JBoss loads on start up. Both our web application and Adobe LiveCycle need to create connections to this data source.

Code

I am glossing over error checking and class structure in this code snippet to focus on the heart of the matter. Hopefully that does not cause problems for others. Text in square brackets is not actual text.

Our code to create the connection is something like this:

Properties props = new Properties();
FileInputStream in = null;
in = new FileInputStream(System.getProperty("[Properties File Alias]"));
props.load(in);
String dsName = props.getProperty(“[JNDI data source name here]”); 
InitialContext jndiCntx = new InitialContext();
DataSource ds = (DataSource) jndiCntx.lookup(dsName);
Ds.getConnection();

I want to be able to test methods dependent upon this code without making any changes to it.

Reference to properties file alias in properties-service.xml file:

  <!-- ==================================================================== -->
  <!-- System Properties Service                                            -->
  <!-- ==================================================================== -->

  <!-- Allows rich access to system properties.-->

<mbean code="org.jboss.varia.property.SystemPropertiesService" 
 name="jboss:type=Service,name=SystemProperties">
  <attribute name="Properties">
    [Folder Alias]=[filepath1]
    [Properties File Alias]=[filepath2]
  </attribute>
</mbean>

Snippet from properties file located at filepath2

[JNDI data source name]=java:/[JNDI data source name]

The JNDI xml file for this data source is set up like this:

<datasources>
  <local-tx-datasource>
    <jndi-name>[JNDI data source name here]</jndi-name>
    <connection-url>jdbc:teradata://[url]/database=[database name]</connection-url>
    <driver-class>com.teradata.jdbc.TeraDriver</driver-class>
    <user-name>[user name]</user-name>
    <password>[password]</password>
    <!-- sql to call on an existing pooled connection when it is obtained from pool -->
    <check-valid-connection-sql>SELECT 1+1</check-valid-connection-sql>
  </local-tx-datasource>
</datasources>

My thoughts of where the solution may be

Is there something I can do in a @BeforeClass method in order to make the properties the above code is looking for available without JBoss? Maybe somehow using the setProperty method of the java.util.Properties class? I would also like to use the same JNDI xml file that JBoss reads from, if possible, in order to reduce duplicate configuration settings.

So far all of my research ends with the advice “Use Spring”, but I don’t think we’re ready to open that can of worms yet. I am not an expert in JBoss, but if more details of our JBoss setup are needed for a helpful answer, I will do my best to get them, though I will likely need some pointers on where to look.

Stackoverflow Research references:
Jndi lookup in junit using spring
Out of container JNDI data source
Other research references:
http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Properties.html
http://docs.oracle.com/javase/jndi/tutorial/basics/prepare/initial.html

Answer

weltraumpirat picture weltraumpirat · Oct 24, 2012

There's a very simple answer to your problem, but you're not going to like it: Don't.

By definition, a unit test should verify the functionality of a single unit (the size of which may vary, but it should be self-sufficient). Creating a setup where the test depends upon web services, databases, etc. is counter-productive: It slows down your tests, it includes a gzillion of possible things that could go wrong (failed network connections, changes to data sets, ...) during the test, which have nothing to do with the actual code you are working on, and most importantly: It makes testing much, much harder and more complicated.

Instead, you should be looking for ways to decouple the legacy code from any data sources, so that you can easily substitute mock objects or similar test doubles while you are testing.

You should create tests to verify the integrity of your entire stack, but those are called integration tests, and they operate at a higher level of abstraction. I personally like to defer writing those until the units themselves are in place, tested and working - at least until you have come to a point where you no longer expect changes to service calls and protocols on a daily basis.

In your case, the most obvious strategy would be to encapsulate all calls to the web service in one or more separate classes, extract an interface that the business objects can depend on, and use mocks implementing that same interface for unit testing.

For example, if you have a business object that calls an address database, you should copy the JNDI lookup code into a new service class called AddressServiceImpl. Its public methods should mimic all the method signatures of your JNDI datasource. Those, then, you extract to the AddressService interface.

You can then write a simple integration test to verify that the new class works: Call all the methods once and see if you get proper results. The beauty of this is that you can supply a JNDI configuration that points to a test database (instead of the original one), which you can populate with test datasets to make sure you always get the the expected results. You don't necessarily need a JBoss instance for this (though I have never had any problems with the eclipse integration) - any other JNDI provider should work, as long as the data source itself behaves the same way. And to be clear: You test this once, then forget about it. At least until the actual service methods ever change.

Once you verified that the service is functional, the next task is to go through all the dependent classes and replace the direct calls to the datasource with calls to the AddressService interface. And from that point on, you have a proper setup to implement unit tests on the actual business methods, without ever having to worry about things that should be tested elsewhere ;)

EDIT

I second the recommendation for Mockito. Really good!