Monday, March 9, 2009

 

Testing Framework with TestNG, EMMA & Maven


Any person who has been working in software industry for some time might have heard about the term 'Unit Testing'. Simply, Unit Testing is a methodology used by software programmers to test their own code before software is delivered to Quality Assurance department. Although, this was initiated as a practice, nowadays, it has become more or less a standard of software development. Code Coverage is also important in software programming. It can be considered as the measure of how well you have tested your own code. There are plenty of frameworks and tools available to write tests and perform code coverage. A simple testing framework could be developed with TestNG (test framework), EMMAMaven 1.x (Build tool).
Let's see how this is achieved step by step. (I have used java 5 for development. TestNG also supports java 1.4)
(code coverage tool) &

Step 1:
Download testng-5.8-jdk15.jar, emma.jar & emma_ant.jar. Copy jars to local repository and add it to your maven project.xml as a dependancy.(Java 1.4 users should download testng-5.8-jdk14.jar)


<dependency>
  <groupId>emma</groupId>
  <id>emma</id>
  <version>1.0</version>
</dependency>
<dependency>
  <groupId>emma_ant</groupId>
  <id>emma_ant</id>
  <version>1.0</version>
</dependency>
<dependency>
  <groupId>testng</groupId>
  <id>testng</id>
  <version>5.8-jdk15</version>
</dependency>
Step 2:
Write a simple test class. If you work on java 1.4, you will not be able to use java 5 annotations. But, TestNG supports XDoclet JavaDoc annotation syntax also.


package simple.test;

import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;

public class SimpleTest {
@DataProvider(name = "numberAddProvider")
  public Object[][] getNumbersToAdd() {
    return new Object[][] {
{new Integer(1), new Integer(1), new Integer(2)},
{new Integer(2), new Integer(2), new Integer(4)},
{new Integer(3), new Integer(3), new Integer(6)}};
}

@Test(dataProvider = "numberAddProvider")
  public void testNumberAddition(Integer n1, Integer n2,
Integer result) {
assert n1.intValue()+n1.intValue()==result.intValue();
}
}
Step 3: Define test suite in testng.xml.

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Simple suite" verbose="2">
<test name="Simple-Test1">
<classes>
<class name = "simple.test.SimpleTest"/>
</classes>
</test>
</suite>

Step 4:
Include a goal to run tests written using TestNG to your build.
you should have added 'emma.enabled=true' to your build.properties so that goals associated with code coverage would be executed when tests are run. I have used offline code coverage. (Instrumentation of classes and collecting run time coverage information are done in two phases. This is the ideal scenario for most of J2EE applications where code is built and deployed in different locations)


<goal name="testng-test" description="Runs testng tests">

<ant:delete dir="${maven.test.dest}"/>
<ant:delete dir="${maven.build.dir}/test-output"/>

<!-- Define classpath (for compilation)-->
<ant:path id="cpath">
<ant:path refid="maven.dependency.classpath"/>
</ant:path>

<!-- Create new output directories-->
<ant:mkdir dir="${maven.test.dest}" />
<ant:mkdir dir="${maven.build.dir}/test-output" />

<!-- Define classpath (for test running)-->
<ant:path id="runpath">
<ant:path refid="cpath"/>
<ant:path refid="maven.dependency.classpath"/>
<ant:pathelement location="${maven.test.dest}"/>
</ant:path>

<!-- Compile test classes (using java 5)-->
<ant:javac srcdir="${basedir}/simple/test"
destdir="${maven.test.dest}" debug="true"
classpathref="cpath" source="1.5"/>

<!-- Defind ant task for runnig TestNG tests-->
<ant:taskdef name="testng" classname="com.beust.
testng.TestNGAntTask" classpathref="cpath"/>

<!-- Run TestNG tests -->
<testng fork="yes" classpathref="runpath"
outputDir="${maven.build.dir}/test-output">
<xmlfileset includes="${maven.test.include.files}"
dir="${basedir}"/>
<jvmarg value="-ea" />
<jvmarg value="-Demma.coverage.out.file=
${maven.build.dir}/test-coverage/coverage.ec" />
</testng>

<!--Generate coverage reports if EMMA is enabled-->
<j:if test="${emma.enabled}">
<attainGoal name="emma-report"/>
</j:if>
</goal>
Following maven goals can be used to instrument classes at build time.

<goal name="emma-init" description="Initialize EMMA for
code coverage">
<ant:path id="emma.classpath">
<ant:path refid="maven.dependency.classpath"/>
</ant:path>
<ant:taskdef resource="emma_ant.properties"
classpathref="emma.classpath" onerror="fail"/>
</goal>

<goal name="emma-instrument" prereqs="emma-init"
description="Instrument the code which needs code
coverage once tests are run using EMMA">
<j:if test="${emma.enabled}">
<ant:mkdir dir="${maven.build.dir}/test-coverage"/>
<ant:emma enabled="${emma.enabled}">
<ant:instr mode="overwrite" metadatafile=
"${maven.build.dir}/test-coverage/coverage.em">
<instrpath>
<ant:path location="${maven.build.dest}"/>
</instrpath>
</ant:instr>
</ant:emma>
</j:if>
</goal>
The goal specified below generates the coverage report.

<goal name="emma-report" prereqs="emma-init"
description="Generage coverage reports using EMMA">

<j:if test="${emma.enabled}">
<ant:emma enabled="${emma.enabled}">
<report depth="method">
<ant:sourcepath>
<ant:pathelement location="${basedir}/src"/>
<ant:pathelement location="${basedir}/simple/
test"/>
</ant:sourcepath>

<infileset dir="${maven.build.dir}/test-coverage/"
includes="*.em, *.ec"/>
<html outfile="${maven.build.dir}/test-coverage/
coverage.html"/>
</report>
</ant:emma>
</j:if>
</goal>
In J2EE applications, you can instrument the classes at build time. The instrumentation metadata file would be created in your local machine. The instrumented version of classes can be packaged in an EAR which ends up deployed in an application server. When you connect to application server and execute functionality provided, coverage data is collected automatically. Once the JVM is closed you will be able to see the coverage data file. The coverage data file can be dowloaded to local folder where instrumentation metadata file resides. The goal defined above could be used to generate the coverage reports providing instrumentation metadata file and coverage information file as arguments.

Labels: , , , ,


Comments: Post a Comment

Subscribe to Post Comments [Atom]




<< Home

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]