-
The Call of ChrismathuluR.J. LorimerFri, Nov 7 2008 @ 2:38 pm
-
Getting True Java Classes in JRubyR.J. LorimerThu, Sep 25 2008 @ 6:41 pm
-
JRuby 1.1.4 ReleasedR.J. LorimerFri, Aug 29 2008 @ 5:41 am
-
My XBoxR.J. LorimerMon, Aug 25 2008 @ 3:22 am
-
Why So Serious?R.J. LorimerSat, Aug 23 2008 @ 5:25 am
Testing: Running Tests Against a Relational Database
Unit tests are an extremely valuable component to most application development. Generally speaking, being able to develop isolated unit tests that focus on one component of Java code is ideal.
When writing tests of different granularity, it isn't always favorable to isolate the component you are testing from remote systems like the database. In fact, not only is it not always favorable, it sometimes isn't even easy or possible given the architecture of the application. Some people would prefer to call a test that interacts with the remote system a system test or functional test rather than a unit test, but the reality is this kind of test is still very valuable when it comes to automated application testing.
One case when this type of testing is often valuable is when your application has a good amount of relational database mapping code; it's not uncommon for an enterprise application to have a large code-base devoted to mapping data back and forth into the database via JDBC. It's not really practical to assert the function of this code without letting it interact with the databases. Whether that layer is implemented with Hibernate, direct JDBC, or one of many other frameworks, it is bound to be a very complex layer of your application, and is an ideal candidate for having automated testing.
Unfortunately, databases, by their nature, are generally a shared environment that persist data long-term, and because of that it is easy to get into a mess of brittle dependencies, possibly with hardcoded primary keys embedded into your tests; effectively making your automated tests extremely dependent on state of an external environment that is more than likely used for much more than just unit tests.
Many people recommend starting an embeddable Java database (such as HSQL ) to run the unit tests against, and while this option works, there is a lot of configuration overhead, and you also are more than likely testing apples to oranges (unless you are running HSQL as your production DB as well, it is very likely it will respond differently to certain queries than, say, Oracle).
Another fairly practical option is to handle test dependencies as part of the unit test itself (in other words, assume you are running on an empty database), and set your unit test up to always roll back the transaction (or connection) at the completion of the test. Here is a relatively contrived example meant to exercise the DAO implementation for a
UserGroup
object and a corresponding
User
object, along with the database implementation code backing those classes. Of most note are the 'setUp', 'tearDown', and 'loadDependencies' methods, as they show the key parts of this form of testing:
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class ExampleTestCase {
private DaoFactory daoFactory;
private Transaction transaction;
private UserGroup group;
private UserGroupDao groupDao;
@Before
public void setUp() throws Exception {
daoFactory = DaoFactory.getInstance();
transaction = daoFactory.beginTransaction();
loadDependencies();
}
/**
* Handle any commonly needed objects.
*/
private void loadDependencies() {
group = new UserGroup("test group", "Test users only.");
groupDao = daoFactory.getUserGroupDao();
groupDao.insert(group);
}
@Test
public void testGroup() {
group = groupDao.getGroup("test group");
assertNotNull(group);
assertEquals("test group", group.getName());
}
@Test
public void testNewUser() {
addUserToGroup();
// Re-load the group, etc.
assertUserInGroup("test group", "test user");
}
@Test
public void testUpdateUser() {
addUserToGroup();
group = groupDao.getGroup("test group");
user = groupDao.getUser("test user");
user.setName("test user rename");
groupDao.update(group);
assertUserInGroup("test group", "test user rename");
}
private void addUserToGroup() {
User user = new User("test user");
group.addUser(user);
groupDao.update(group);
}
private void assertUserInGroup(String groupName, String userName) {
group = groupDao.getGroup(groupName);
assertNotNull(group);
assertEquals(groupName, group.getName());
user = group.getUser(userName);
assertNotNull(user);
assertEquals(userName, user.getName());
}
@After
public void tearDown() throws Exception {
transaction.rollback();
}
}
In this example, the two test methods both will have 'loadDependencies' called on a 'clean' database; in other words, because at the completion of each test the transaction is rolled back, the 'group' object will be recreated and repersisted in each pass, yet at the completion of the entire test case, no data will be permanently stored in teh database.
