I have no special talent. I'm only passionately curious - Albert Einstein
Unit Testing Comment on Unit Testing 0

Unit testing not only ensures that application code runs the way the developer intends it to, but can also provide a sort of guide for other developers. Unit testing helps to make an application more robust - running existing unit tests assists in revealing impacts changes may have on an application, assuring against defects. Any applications that don't have unit tests are considered Legacy Applications, and are usually more expensive to change, and have experienced more defects, adding into the break/fix cycle in production deployments. I prefer to implement unit tests using JUnit, since there is plenty of extensions and frameworks that support writing and running JUnit tests. EasyMock is a JUnit library that allows developers to unit test code which contains many dependancies, and provides a means to focus on actual unit tests instead of functional or integration testing. An excellent Unit test code coverage tool exists as a plugin for Eclipse, EclEmma, which provides package level, class level, and line by line statistics of unit test coverage in Java classes. The Spring Framework provides many utilities to unit test Spring configured code as well. Here are some example unit tests

JUnit 4

/**
* JUnit 4 looks for the @Before, @After, and @Test annotations to drive tests, and
* doesn't require your test class to extend any JUnit classes
*/
public class SampleServiceTest{

private SampleService service;

@Before
public static void preTest(){
//execute any test prep code here, like opening a
//jdbc connection, or beginning a transaction
service = new SampleService();
}

@After
public static void postTest(){
//execute any post-test code here, like closing a
//jdbc connection, or ending a transaction
}

@Test
public void testExecute(){
Object result = service.execute();
Assert.assertNotNull("result was null!", result);
}

/**
* JUnit 4 adds a nice new feature - you can tell the test annotation to expect any
* exception as an acceptable part of your testing. This will actually fail if the
* expected exception isn't thrown, therefore ensuring exception handling is
* worked as expected
*/
@Test(expected=Exception.class)
public void testSaveNullThrowsException(){
service.save(null);
}

}

Spring Dependency Injected Transaction Managed Integration Test

/**
* Spring will automatically wrap any test cases in this test class in a transaction, using
* a transaction manager. There's a Java5 way to do this with annotations as well
*
*/
public class EmployeeDaoTest extends AbstractTransactionalDataSourceSpringContextTests {

private static final Logger logger = Logger.getLogger(EmployeeDaoTest.class);
protected EmployeeDao employeeDao;

public EmployeeDaoTest(){
/*
* this is new for Spring 2.5. If you leave this to the default (true),
* you may have problems, as Spring will try to autowire by type
*/
setDependencyCheck(false);
/*
* Tell Spring whether to roll back the transaction on test completion. This makes your
* tests repeatable when saving data to a database
*/
setDefaultRollback(true);
/*
* Tell Spring to attempt to dependency inject protected variables in this class. Will
* look in the Spring context for beans with the same name as the variable
*/
setPopulateProtectedVariables(true);
}


// specifies the Spring configurations to load for this test
protected String[] getConfigLocations() {
return new String[] {
"test-dao.context.xml"
};
}

public void testFindByID() {
Long id = new Long(1357252L);
Employee employee = employeeDao.findByID(id);
assertNotNull(employee);
}

public void testFindByIDWithInvalidIDReturnsNull() {
Long id = Long.MAX_VALUE;
Employee employee = employeeDao.findByID(id);
assertNull(employee);
}

public void testQueryByExampleFirstName(){
Employee example = new Employee();
String firstName = "Dave";
example.setFirstName(firstName);
List employees = employeeDao.queryByExample(example);
assertNotNull(employees);
logger.debug("Employee QBE first name of " + firstName + " returned " + employees.size() + " employees");
for(Employee employee : employees){
assertNotNull(employee.getFirstName());
assertTrue(employee.getFirstName().toUpperCase().startsWith(firstName.toUpperCase()));
}
}

}

Unit testing with EasyMock (JUnit 4, Java 5+)

/**
* Tests for EmployeeService. Uses EasyMock to mock out the dependancies EmployeeService has
* on ManagerDao and EmployeeDao, and measures the calls into those dependancies,
* and the expected return types
*
* @author Dave Malone
*/
public class EmployeeServiceTest {

private EmployeeService service;
private ManagerDao mockManagerDao;
private EmployeeDao mockEmployeeDao;

@Before
public void setUp() throws Exception {
//create mock dao objects, the dependencies EmployeeService relies on
mockManagerDao = org.easymock.classextension.EasyMock.createMock(ManagerDao.class);
mockEmployeeDao = org.easymock.classextension.EasyMock.createMock(EmployeeDao.class);
//instantiate the service, giving it the mocked dependencies
service = new EmployeeService(mockManagerDao, mockEmployeeDao);
}

/*
* EasyMock allows you to create mock objects, and set them in 'replay mode'. Once
* an object is set into replay mode, it's ready to be used and evaluated in the rest of the
* system. In this example, we create a mock Employee object, and an employeeId. We tell
* EasyMock that we expect the mock employeeDao object's findById method, with the employeeId
* argument, and to return the mock Employee object exactly once. These are the method calls
* and expected returned objects that would be called within the EmployeeService.getEmployeeById
* method call
*/
@Test
public void testGetEmployeeById() {
final Long employeeID = 123L;
final Employee mockEmployee = org.easymock.classextension.EasyMock.createMock(Employee.class);
org.easymock.classextension.EasyMock.replay(mockEmployee);
org.easymock.classextension.EasyMock.expect(mockEmployeeDao.findByID(employeeID)).andReturn(mockEmployee).once();
org.easymock.classextension.EasyMock.replay(mockEmployeeDao);

final Employee result = service.getEmployeeById(employeeID);
Assert.assertEquals(mockEmployee, result);
}


}

Finally, you can add some code quality tools to your code with Cobertura reports and Ant. Ant has a JUnit task which you can use to run all of your JUnit tests. You can use Cobertura to automatically generate code coverage statistics reports as part of your build. See this example (copied directly from the Cobertura website:

 

<junit fork="yes" dir="${basedir}" failureProperty="test.failed">
<!--
Specify the name of the coverage data file to use.
The value specified below is the default.
-->
<sysproperty key="net.sourceforge.cobertura.datafile"
file="${basedir}/cobertura.ser" />


<!--
Note the classpath order: instrumented classes are before the
original (uninstrumented) classes. This is important.
-->
<classpath location="${instrumented.dir}" />
<classpath location="${classes.dir}" />

<!--
The instrumented classes reference classes used by the
Cobertura runtime, so Cobertura and its dependencies
must be on your classpath.
-->
<classpath refid="cobertura_classpath" />

<formatter type="xml" />
<test name="${testcase}" todir="${reports.xml.dir}" if="testcase" />
<batchtest todir="${reports.xml.dir}" unless="testcase">
<fileset dir="${src.dir}">
<include name="**/*Test.java" />
</fileset>
</batchtest>
</junit>

0 comments

Add a comment

Please provide your name, email address (won't be published) and a comment

About

David Malone is a Java developer residing in the Twin Cities area.  He has been developing enterprise applications since 2004.  This is his personal blog, as well as his design and development workspace.