One of the best practice for developing application is Test
Driven Development. Common most tool/api we use in Java for unit testing is JUnit
and for automation we use is Ant. Ant task for JUnit supports four result
format.
- · xml
- · pPlain
- · brief
- · failure
Details
about these formats can be found at JUnit task document. But at times we need
to get unit test results in format other than default format. In my case I
wanted to store these results in csv/xls format to keep track of results that
have passed/failed/thrown error during development. So we created our own
format by implementing org.apache.tools.ant.taskdefs.optional.junit.JUnitResultFormatter.
For implementing this interface we need to have following jars in classpath
·
ant.jar
·
ant-junit.jar
·
junit-x.y.jar (Where x and y are version and
subversion of jar)
So
we create a java project in Eclipse and add these jars in build path and create
following class in java project.
package com.customization.ant.junit.formatter;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Vector;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
import org.apache.tools.ant.BuildException;
import
org.apache.tools.ant.taskdefs.optional.junit.JUnitResultFormatter;
import org.apache.tools.ant.taskdefs.optional.junit.JUnitTest;
import
org.apache.tools.ant.taskdefs.optional.junit.JUnitVersionHelper;
import com.customization.ant.junit.bean.UnitTestBean;
public class XLSFormatter implements
JUnitResultFormatter{
private enum TestStatus {PASS,FAIL,ERROR};
boolean testsuccess = true;
boolean isError = false;
private OutputStream ostrm;
private
List<UnitTestBean> beanlist = new Vector<UnitTestBean>();
@Override
public void addError(Test
test, Throwable t) {
testsuccess=false;
isError = true;
}
@Override
public void addFailure(Test t,
AssertionFailedError e) {
testsuccess=false;
System.out.println("Adding
failure");
}
@Override
public void endTest(Test test)
{
String
caseName = JUnitVersionHelper.getTestCaseName(test);
UnitTestBean
bean = new UnitTestBean();
bean.setCaseName(caseName);
bean.setSuccessful(testsuccess);
beanlist.add(bean);
}
@Override
public void startTest(Test
test) {
testsuccess = true;
isError = false;
}
@Override
public void
endTestSuite(JUnitTest test) throws BuildException {
StringBuffer
sbuff = new StringBuffer();
sbuff.append("TestCase
Name,Status\n");
for(UnitTestBean bean:beanlist)
{
sbuff.append(bean.getCaseName()).append(",").append(bean.isSuccessful()?TestStatus.PASS:
isError?TestStatus.ERROR:TestStatus.FAIL).append("\n");
}
try
{
ostrm.write(sbuff.toString().getBytes());
ostrm.flush();
ostrm.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void
setOutput(OutputStream ostrm) {
this.ostrm = ostrm;
}
@Override
public void
setSystemError(String arg0) {
isError=true;
}
@Override
public void
setSystemOutput(String arg0) {
}
@Override
public void
startTestSuite(JUnitTest test) throws BuildException {
}
}
|
Let’s
understand the code now. We will define following class level variables to
store status of test cases.
private enum TestStatus {PASS,FAIL,ERROR};
boolean testsuccess = true;
boolean isError = false;
testsuccess will be set
to true if test case is executed successfully. If test case fails then addFailure(Test t,
AssertionFailedError e) will be called and we will set variable testsuccess to false.
Default value of isError will
be set to false and if any Exception/Error occurs while executing test case
then addError(Test
test, Throwable t) will be called and in this method we will set isError to true.
When
we implement org.apache.tools.ant.taskdefs.optional.junit.JUnitResultFormatter
interface we need to implement following methods.
- addError(Test, Throwable): As explained this method will be called when any error occurs while executing test case. We will set isError to true if/when this method is called.
- addFailure(Test, AssertionFailedError): As explained this method will be called when any test case fails. We will set testsuccess to false if/when this method is called.
- endTest(Test): This method will be called at the end of every test. We will use this method to set the value of testcase name and status of this test case (i.e. Pass/Fail/Error) and add it in a Collection (Vector in our case)
- endTestSuite(JUnitTest): This method will be called at the end of Test suite. We will use this method to write result of all tests in csv file.
- setOutput(OutputStream): Output stream as defined in ant task will be passed as an argument. We will store this as handler to local variable to use it later for writing our test results.
- setSystemError(String): This method is not used in our case.
- setSystemOutput(String): We are not using this method either.
- startTest(Test): Not this one too…
- startTestSuite(JUnitTest): Ok…not even this one J
Now
compile this class and export it as a jar. Add this jar in project where we
want to run test cases. In JUnit ant task of build file add following as
formatter. (Following is simplest of options to tell the build file to use this
new class for formatting results)
<formatter classname="com.customization.ant.junit.formatter.XLSFormatter" extension=".csv" />
|
Here we are using a very simple JUnit test case for employee
registration. Where method accepts data in form of a bean and registers
employee. If passed bean is null or values passed in bean are invalid then it
will return false and if bean is valid then employee will be registered and
method will return true. So following will be test class.
package com.sampleapp.service.test;
import org.junit.Test;
import com.sampleapp.service.EmployeeRegistrationService;
import com.sampleapp.vo.EmployeeBean;
import junit.framework.TestCase;
public class EmployeeServiceTest extends TestCase
{
EmployeeBean empBean=null;
EmployeeRegistrationService
svc = new
EmployeeRegistrationService();
@Override
protected void setUp() throws Exception {
System.out.println(getName());
super.setUp();
}
@Test
public void
testForNullEmployee()
{
boolean retval = svc.registerEmployee(null);
assertFalse("Registered
Null employee",retval);
}
@Test
public void
testForValidEmployee()
{
empBean = new EmployeeBean();
boolean retval = svc.registerEmployee(empBean);
assertTrue("Not able to
registered valid employee",retval);
}
}
|
When
we will run this test case following will be output on console
Apache Ant(TM) version 1.8.3 compiled on February 26 2012
Buildfile: E:\softwares\java\workspaces\keyur\Blogs\TestDrivenDevelopment\src\build.xml
parsing buildfile E:\softwares\java\workspaces\keyur\Blogs\TestDrivenDevelopment\src\build.xml
with URI =
file:/E:/softwares/java/workspaces/keyur/Blogs/TestDrivenDevelopment/src/build.xml
Project base dir set to:
E:\softwares\java\workspaces\keyur\Blogs\TestDrivenDevelopment\src
parsing buildfile jar:file:/E:/softwares/java/eclipse-jee-juno-SR1-win32/eclipse/plugins/org.apache.ant_1.8.3.v20120321-1730/lib/ant.jar!/org/apache/tools/ant/antlib.xml
with URI =
jar:file:/E:/softwares/java/eclipse-jee-juno-SR1-win32/eclipse/plugins/org.apache.ant_1.8.3.v20120321-1730/lib/ant.jar!/org/apache/tools/ant/antlib.xml
from a zip file
[echo] Testing
[junit]
[junit]
BUILD SUCCESSFUL
Total time: 287 milliseconds
|
And
following will be the file generated
So
this generated data was stored in following format to track results of each
build.
So happy
coding.