Here is what the code does.
- Get a database connection using JNDI (from the app container)
- Does some database stuff
Here are my self imposed goals for the unit tests:
- Runnable out of the box. (i.e. if you can checkout and build the project, you can run the unit tests)
- Does not require an app container. I don't want to require a running instance of JBoss just to have access to JNDI so I can run unit tests.
- Each developer has their own unit testing database sandbox to run unit tests in, so they won't/can't collide with each other.
- Ideally the unit testing database is checked into the repository.
These seemed like reasonable goals to me, and I figured a few minutes of Google searching would give a couple of options of how this is typically done. Since it took more than that, I'll write up what I did.
HSQLDB
StackOverflow led me to HSQLDB. With HSQLDB I am able to get an in-memory database, which is perfect for unit testing. And I can make sure all other team members can use it, just by including the jar file in the check-in. Well, that seems easy enough.
JNDI
Now all I had to do was to make the HSQLDB DataSource available via JDBC. This should be easy enough, right? Right? Ugh.
I found this blog that seems to be the standard way to inject a JNDI datasource without an app container. Seems easy enough, except that when I tried to follow the directions, I got a ClassNotFoundException for org.apache.naming.java.javaURLContextFactory. It seems that this class is included with Tomcat. I suppose I could've gone and gotten the Tomcat jar to include in the project, but this didn't feel right with my goal to be app container agnostic.
So with a little more searching I found an in-memory JNDI context on SourceForge. Well, that seems to be what I want.
[edit - As I wrote in my follow up post, Don't Reuse. Rewrite!, I ended up writing my own in memory JNDI context, since all I needed were the bind and lookup methods.]
Huh - now that I write it up, it seems much less painful than it was when I was trying to track all this down myself.
Implementation Summary
Download two packages:
- HSQLDB - http://hsqldb.org/index.html
In Memory JNDI - http://sourceforge.net/projects/memoryjndi/[edit - don't use any more]
I added 3 jars to my CLASSPATH.
- hsqldb.jar - comes with the HSQLDB download.
jndi.jar - comes with the in-memory JNDI context.[edit - don't use any more]util.jar - also with the in-memory JNDI context. (and yes - they really did name the jar file unit.jar)[edit - don't use any more]
As for the actual unit testing code, I am using JUnit 4. This means that I can put the setup code once per class. As such my code looks something like:
1 import java.sql.Connection;
2 import java.sql.Statement;
3
4 import javax.naming.Context;
5 import javax.naming.InitialContext;
6 import javax.sql.DataSource;
78 import jndi.naming.provider.MemoryContextFactory;
8 import mypackage.MockInitialContextFactory;
9
10 import org.hsqldb.jdbc.JDBCDataSource;
11 import org.junit.BeforeClass;
12
13 public class MyUnitTest {
14 private static final String JNDI_NAME="my_JNDI_name";
15
16 @BeforeClass
17 public static void setUpBeforeClass() throws Exception {
18 createJNDIContext();
19 createDBTable();
20 }
21
22 public static void createJNDIContext() throws Exception {
23 JDBCDataSource ds = new JDBCDataSource();
24 ds.setDatabase("jdbc:hsqldb:mem:mymemdb");
25 ds.setUser("SA");
26
27 System.setProperty(Context.INITIAL_CONTEXT_FACTORY,28 MemoryContextFactory.class.getName());
28 MockInitialContextFactory.class.getName());
29 InitialContext ic = new InitialContext();
30 ic.bind(JNDI_NAME, ds);
31 }
32
33 private static void createDBTable() throws Exception {
34 DataSource ds = (DataSource)new InitialContext().lookup(JNDI_NAME);
35 Connection conn = ds.getConnection();
36 String create = "CREATE TABLE MY_TABLE ("
37 // Insert column description here.
38 + " )";
39 Statement stmt = conn.createStatement();
40 stmt.executeUpdate(create);
41 conn.close();
42 }
43
44 // Unit Tests go here
45 }
46
Note - this code was colorized by: http://puzzleware.net/codehtmler/default.aspx.
No comments:
Post a Comment