Monday, May 26, 2008

TDD Part 2 . Apache ANT tutorial

Apache ANT describe itself as "a Java-based build tool. In theory, it is kind of like Make, but without Make's wrinkles." Ant is widely used for building Java projects but I feel it is much more than that. For example it can be used for constructing a portable build system for a C/C++ project. (as make replacement). Simply said Ant is a scripting and automation tool based on XML.

I understand TDD as a way of developing software in which Automation plays a very important role. Automation can be a suite of JUnits which will run with every build to verify that nothing is broken, or importing a big database dump for preparing a performance testing scenario, or automatically playing some GUI recorded scripts to make some functional testing or whatever. In all these scenarios ANT can help a lot.

Enough with theory...let's get our hands dirty with some real stuff now.

Starting with a good build system plays a very important role in every successful TDD project. Following is a picture presenting a classic ANT view of a build script in Eclipse IDE

Each node represents an ANT target. This is a clear "unit of work" with a specific scope in the final build process. A target can depend on some other targets so it is very easy to define a complex order of execution of ant targets that will fit best each need. Ant scripts are written in xml files. By default the name of the xml file is build.xml. So let's take one by one each target and describe in detail.

1.clean:


<target name="clean" description="Remove all generated files">
<delete dir="${dest.dir}"/>
<delete defaultexcludes="false">
<fileset dir="${src.test.dir}/resources/pdfTestFiles/outputBurstedFiles"/>
</delete>
<delete defaultexcludes="false">
<fileset dir="${src.test.dir}/resources/pdfTestFiles/outputSendedFiles"/>
</delete>
</target>


I think the code is self explanatory. It deletes the output already compiled classes
and it also deletes some output folders which are populated while playing the automated test suite.

2.compile [default]

This is the target which compiles the java source code. It is the default target from the build file which means that it is executed by default when no other target is specified. Here is the code:

<target name="compile" depends="init" description="Compile Java source files">
<javac srcdir="${src.dir}" destdir="${dest.dir}/classes" debug="on" classpathref="buildtime.classpath" deprecation="on" encoding="ISO-8859-1" />
</target>


As you see this target depends on the target "init". This means that "init" will be first executed when the "compile" target is called. So let's see the "init" target.

3.init:

<target name="init">
<mkdir dir="${dest.dir}/classes"/>
<mkdir dir="${dest.dir}/test-classes"/>
<mkdir dir="${dest.dir}/jar"/>
<mkdir dir="${dest.dir}/zip"/>
<mkdir dir="${dest.dir}/junit-report"/>
</target>


The code is very simple and straightforward. It just creates the output folders that are needed by the compile target.

4.compile.tests:

This is the code which compiles the JUnit test cases. It is just a call to the java compiler with the Ant task and it depends on the simple "compile" to be called before. The junit test cases have as a dependency the main program classes therefore these should be compiled before.

<target name="compile.tests" depends="compile">
<javac classpathref="testtime.classpath" destdir="${dest.dir}/test-classes" srcdir="${src.test.dir}" debug="on" deprecation="on" encoding="ISO-8859-1"/>
</target>


5.run.tests:

This is the target which actually runs the JUnit tests. Of course it depends on compiling the tests so it has depends="compile.tests". Here is the code:

<target name="run.tests" depends="compile.tests">
<junit printsummary="yes" haltonfailure="yes">
<classpath>
<path refid="testtime.classpath"/>
<pathelement path="${dest.dir}/test-classes"/>
</classpath>
<batchtest haltonfailure="no" todir="${dest.dir}/junit-report">
<formatter type="xml"/>
<fileset dir="${dest.dir}/test-classes">
<exclude name="**/TestUtilities.class"/>
<exclude name="**/*$*.class"/>
</fileset>
</batchtest>
</junit>
</target>


So how this target is working? It is calling the junit task for running the tests, it is setting the classpath to the testtime.classpath and it is giving as input "a batch" of already compiled tests which are in the "${dest.dir}/test-classes".

6-7.make.jar and make.zip:

These two targets are much related with packaging and distribution. make.jar as the name suggest is packaging the compiled classes as a single jar file. This target depends on the target "run.tests" which means that the jar is created only if all the JUnit test cases are passing successfully. Here is the code for "make.jar":

<target name="make.jar" depends="run.tests">
<jar destfile="${jar.file}">
<fileset dir="${dest.dir}/classes"/>
<manifest>
<attribute name="Main-Class" value="burster.MainBurster"/>
<attribute name="Class-Path" value="lib/PDFBox-0.7.3.jar lib/oro-2.0.8.jar
lib/mail-1.4.1.jar lib/fontbox-0.1.0.jar lib/commons-validator-1.3.1.jar
lib/commons-net-1.4.1.jar lib/commons-email-1.1.jar lib/jta.jar
lib/commons-digester-1.7.jar
lib/commons-beanutils.jar lib/commons-logging.jar lib/commons-logging-api.jar
lib/commons-collections-3.1.jar lib/quartz-1.6.0.jar"/>
</manifest>
</jar>
</target>


Jar task is used for creating the output file. "Main-Class" and "Class-Path" are the "attributes" which are putted in the manifest file when defining the jar.

make.zip - This is the target which takes the output DocumentBurster.jar, it copies also the lib files which are needed, the src files,some txt files like LICENSE.txt, README.txt, Prerequisites.txt and some other and is building the final zip files which will be downloaded by people. Here is the code:

<target name="make.zip" depends="make.jar">
<delete includeemptydirs="true">
<fileset dir="${dest.dir}/zip" includes="**/*"/>
</delete>
<mkdir dir="./tmp"/>
<copy todir="./tmp/bin/lib">
<fileset dir="${runtime.lib.dir}">
</fileset>
</copy>
<copy todir="./tmp/src">
<fileset dir="../DocumentBurster_client/src">
</fileset>
</copy>
<copy file="${resources.dir}/config/settings.xml" tofile="./tmp/bin/config/settings.xml"/>
<copy file="${resources.dir}/config/quartz-jobs.xml" tofile="./tmp/bin/config/quartz-jobs.xml"/>
<copy file="${jar.file}" tofile="./tmp/bin/${project.name}.jar"/>
<copy file="${bat.file}" tofile="./tmp/bin/${project.name}.bat"/>
<copy file="${resources.dir}/txt_files/LICENSE.txt" tofile="./tmp/LICENSE.txt"/>
<copy file="${resources.dir}/txt_files/README.txt" tofile="./tmp/README.txt"/>
<copy file="${resources.dir}/txt_files/Prerequisites.txt" tofile="./tmp/Prerequisites.txt"/>
<copy file="../DocumentBurster_client/IDEs_proj_files/VS2005/DocumentBurster/DocumentBurster/Release/DocumentBurster.exe" tofile="./tmp/bin/DocumentBurster.exe"/>
<copy file="../DocumentBurster_client/src/resources/mondrian.ico" tofile="./tmp/bin/images/mondrian.ico"/>
<copy file="../DocumentBurster_client/src/resources/DocumentBurster.exe.Manifest" tofile="./tmp/bin/DocumentBurster.exe.Manifest"/>
<zip destfile="${zip.file}" basedir="./tmp"/>
<delete dir="./tmp"/>
</target>


8.test.gui.with.autoit. This is another target related with automated testing. "run.tests" were running only the JUnit test cases which were testing each java class in isolation. This is taking the redistributable zip file and runs and AutoIt GUI recorded script. This is more like a functional testing comparing with "run.tests" which was about unit testing. Here is the code:

<target name="test.gui.with.autoit" depends="make.zip">
<unzip src="${zip.file}" dest="./tmp"/>
<copy file="../DocumentBurster_client/IDEs_proj_files/AutoIt3/documentburster.au3" tofile="./tmp/bin/documentburster.au3"/>
<copy file="${src.test.dir}/resources/pdfTestFiles/inputFiles/SimpleBurster.pdf" tofile="./tmp/SimpleBurster.pdf"/>
<exec executable="C:\Program Files\AutoIt3\AutoIt3.exe">
<arg value="./tmp/bin/documentburster.au3"/>
</exec>
<delete dir="./tmp"/>
</target>


Most important thing to notice here is the use of the Exec Ant Task which is used to call an external program from ANT.

Here I finished my "hands on" tutorial with ANT. I hope it was a useful piece of information that you will be using with success it in your own projects.

Further resources:
1. Ant in Action

1 comments:

Anonymous said...

http://firstclassthoughts.co.uk/ant/ has a lot of relevant notes on ANT and various tasks to perform. Junit, test coverage, etc.