![]() | ![]() |
Apache > Jakarta > Cactus > Running Tests > Ant | Docs for: v1.7.2 | v1.7 Last update: November 26 2008 |
Continuous integration
This tutorial was written when I wrote Cactus 1.2. Since then
I have improved my command of Ant. However this tutorial has not yet
been updated. I believe it still provides some very good
methodology on how to use Ant, although not the latest. If you wish to
keep track of the latest change, have a look at the Cactus build files,
found in the source distribution.
A strong principle of eXtreme Programming (XP) is the continuous integration aspect (see the Continuous Integration article by Martin Fowler). The traditional approach has been to developing the code first, then test it and then integrate it with other applications. The continuous integration principle is to develop code, test and integrate at the same time, i.e. at any point in time, you have a functioning code along with tests and integrated. In order to be able to do continuous integration, you need to be able to automatically run the build for your application, including passing the unit tests (based on JUnit, Cactus, HttpUnit or others). Ant is the perfect tool for this task. Ant benefitsThe benefits of using Ant for running unit tests are as follows:
Writing an Ant build script tutorialThe sample Ant build file described below is taken from the Cactus Sample for Servlet API 2.2 project.
This section is both a tutorial for automating builds and unit testing
with Ant and Cactus and also a best practice and methodology guide
for using Ant in general (independent of Cactus). This is an Ant
configuration that has been working for me on several project. Of course,
you are free to adapt/modify it to suit your needs.
Project directory structureCreate the following directories:
An
out directory will be created by the Ant build. All
build-generated files will be put in that directory (compiled classes,
generated javadoc documentation, test configuration files for running
an application server, ...).
We don't have any
lib directory because it is always
better not to include dependent jars in your project whenever
possible for the following reasons: better continuous integration
with other libraries (meaning they also evolve and you should test
as much as possible with the latest version to discover potential
problems early, more lightweight downloads, less jar proliferation
(you'll end up with tens of the same jars otherwise), more version
control and integration checks (if your project uses 2 external
libraries that need another third library but not in the same version
you are in trouble !), ...
Ant Target List
Define the following targets in your
The Type column specify whether the target is an external
target that can be called by the user of the build file or if it is
an internal target, intended to be called internally by another
target inside the build script.
Step 1: The Ant project basedir
You Example: <?xml version="1.0"?> [...] <project name="Cactus Sample" default="war" basedir=".."> Step 2: initialization of project propertiesYou should define as properties all values that can be factorized and which are used often in your build file such as source locations, output locations, external jar locations and names, ... You can also define useful file patterns there (see the sample below).
A good principle is to defined any properties that depend on your
environment (such as external jar locations) in a file (let's call
it <!-- Give user a chance to override without editing this file (and without typing -D each time it compiles it) --> <property file="build/build.properties" /> <property file="${user.home}/build.properties" /> Here are our properties initializations: <project name="Cactus Sample" default="war" basedir=".."> <!-- Give user a chance to override without editing this file (and without typing -D each time it compiles it) --> <property file="build/build.properties" /> <property file="${user.home}/build.properties" /> <!-- Generic project properties --> <property name="project.fullname" value="Cactus Sample"/> <property name="project.version" value="1.7.2"/> <property name="project.name" value="cactus-sample"/> <!-- Miscellaneous settings --> <property name="year" value="2000-2004"/> <property name="debug" value="on"/> <property name="optimize" value="off"/> <property name="deprecation" value="off"/> <!-- ======================================================================== Set the properties related to the source tree ======================================================================== --> <!-- Source locations for the build --> <property name="src.dir" value="src"/> <property name="src.java.dir" value="${src.dir}/share"/> <property name="src.java.servlet.dir" value="${src.dir}/servlet@servlet.api@"/> <property name="build.dir" value="build"/> <property name="etc.dir" value="${build.dir}/etc"/> <property name="lib.dir" value="lib"/> <property name="conf.dir" value="conf"/> <property name="conf.test.dir" value="conf/test"/> <property name="web.dir" value="web"/> <!-- ======================================================================== Set the properties related to the build area ======================================================================== --> <!-- Destination locations for the build (relative to the basedir as --> <!-- specified in the basedir attribute of the project tag) --> <property name="out.dir" value="out"/> <property name="out.dist.dir" value="${out.dir}/dist"/> <property name="out.lib.dir" value="${out.dir}/lib"/> <property name="out.test.dir" value="${out.dir}/test"/> <property name="out.src.dir" value="${out.dir}/src"/> <property name="out.classes.dir" value="${out.dir}/classes"/> <property name="out.doc.dir" value="${out.dir}/doc"/> <property name="out.javadoc.dir" value="${out.doc.dir}/javadoc"/> <property name="out.conf.dir" value="${out.dir}/conf"/> <!-- Names of deliverables --> <!-- The Cactus Sample war file. This is the file that should be used at runtime by end users (it excludes the test classes) --> <property name="final.war.name" value="${out.dir}/${project.name}-@servlet.api@.war"/> <!-- The full sources of Cactus Sample in a zip file --> <property name="final.src.name" value="${out.dir}/${project.name}-src-@servlet.api@.zip"/> <!-- The Cactus sample documentation in a zip file --> <property name="final.doc.name" value="${out.dir}/${project.name}-doc-@servlet.api@.zip"/> <!-- ======================================================================== Useful file patterns for targets ======================================================================== --> <!-- All source files of the projet. These source files will be copied to the destination source directory in the prepare task --> <patternset id="all.src.files"> <!-- All java files --> <include name="**/*.java"/> <!-- All doc files --> <include name="**/package.html"/> <include name="**/overview.html"/> <!-- All conf files (including test files) --> <include name="**/*.txt"/> <include name="**/*.xml"/> <include name="**/*.properties"/> </patternset> <!-- All non java files in the src directory --> <patternset id="all.nonjava.files"> <!-- All conf files (including test files) --> <include name="**/*.txt"/> <include name="**/*.xml"/> <include name="**/*.properties"/> </patternset> Step 3: 'init' targetUseful for initializing a timestamp (DSTAMP, TODAY, TSTAMP), defining token filters, printing some information messages, registering custom Ant tasks, ... <!-- ======================================================================== Initialize the build. Must be called by all targets ======================================================================== --> <target name="init"> <!-- So that we can use the ${TSTAMP}, ${DSTAMP}, ... time stamps in targets, if need be --> <tstamp/> <echo message="--------- ${project.fullname} ${project.version} ---------"/> <echo message=""/> <echo message="java.class.path = ${java.class.path}"/> <echo message=""/> <echo message="java.home = ${java.home}"/> <echo message="user.home = ${user.home}"/> <echo message=""/> <echo message="basedir = ${basedir}"/> <echo message=""/> <echo message="servlet.jar = ${servlet.jar}"/> <echo message="cactus.jar = ${cactus.jar}"/> <echo message="junit.jar = ${junit.jar}"/> <echo message="cactus.ant.jar = ${cactus.ant.jar}"/> <!-- Filters --> <filter token="version" value="${project.version}"/> <filter token="year" value="${year}"/> <!-- Initialize custom Ant tasks needed for running the server tests --> <taskdef resource="cactus.tasks"> <classpath> <pathelement location="${cactus.ant.jar}"/> <pathelement path="${java.class.path}"/> </classpath> </taskdef> </target> Step 4: 'usage' targetsDisplay a usage message. <!-- ======================================================================== Help on usage. List available targets ======================================================================== --> <target name="usage" depends="init"> <echo message=""/> <echo message="${project.fullname} build file"/> <echo message="------------------------------------------------------"/> <echo message=""/> <echo message=" Available targets are:"/> <echo message=""/> <echo message=" war --> generates the war file (default)"/> <echo message=" clean --> cleans up the build directory"/> <echo message=" source --> generates source zip of the project"/> <echo message=" doc --> generates the docs (javadoc, ...)"/> <echo message=" all --> do it all at once"/> <echo message=" (clean, war, source, doc)"/> <echo message=""/> <echo message=" Targets for running the tests for Servlet API 2.2:"/> <echo message=""/> <echo message=" tests_resin_12 --> run tests for Resin 1.2"/> <echo message=" tests_tomcat_32 --> run tests for Tomcat 3.2"/> <echo message=" tests_weblogic_51 --> run tests for WebLogic 5.1"/> <echo message=" tests_orion_14 --> run tests for Orion 1.4"/> <echo message=""/> </target> Step 5: 'prepare' target
This target is needed for both compiling and generating the javadoc.
It copies all the source files from their <!-- ======================================================================== Prepare the output directory by copying the source files into it ======================================================================== --> <target name="prepare" depends="init"> <mkdir dir="${out.src.dir}"/> <!-- Copy all source files to destination dir. Apply the filters in order to replace the tokens for the copyright year and the version --> <copy todir="${out.src.dir}" filtering="on"> <fileset dir="${src.java.dir}"> <patternset refid="all.src.files"/> </fileset> <fileset dir="${src.java.servlet.dir}"> <patternset refid="all.src.files"/> </fileset> </copy> </target> Step 6: 'compile' target
The <!-- ======================================================================== Compiles the source directory ======================================================================== --> <!-- Preparation target for the compile target --> <target name="prepare-compile" depends="prepare"> <mkdir dir="${out.classes.dir}"/> </target> <!-- Run the java compilation --> <target name="compile" depends="prepare-compile"> <javac srcdir="${out.src.dir}" destdir="${out.classes.dir}" debug="${debug}" deprecation="${deprecation}" optimize="${optimize}"> <!-- Exclude all files that are not .java source files --> <!-- All doc files --> <exclude name="**/package.html"/> <exclude name="**/overview.html"/> <!-- All conf files (including test files) --> <exclude name="**/*.txt"/> <exclude name="**/*.xml"/> <exclude name="**/*.properties"/> <classpath> <pathelement path="${java.class.path}"/> <pathelement location="${servlet.jar}"/> <pathelement location="${cactus.jar}"/> </classpath> </javac> <!-- Copies non java files that need to be in the classes directory --> <copy todir="${out.classes.dir}"> <fileset dir="${src.java.dir}"> <patternset refid="all.nonjava.files"/> </fileset> <fileset dir="${conf.test.dir}"> <include name="cactus.properties"/> </fileset> </copy> </target> Step 7: 'source' targetZip up the sources for distribution. <!-- ======================================================================== Generates source zip of the project ======================================================================== --> <target name="source" depends="prepare"> <zip zipfile="${final.src.name}" basedir="."> <exclude name="${out.dir}/**"/> <exclude name="**/*.log"/> <exclude name="**/*.bak"/> <exclude name="**/*.class"/> <exclude name="${build.dir}/build.properties"/> </zip> </target> Step 8: 'javadoc' targetGenerate the project's javadoc. <!-- ======================================================================== Generate the javadoc ======================================================================== --> <!-- Preparation target for the javadoc target --> <target name="prepare-javadoc" depends="prepare"> <mkdir dir="${out.javadoc.dir}"/> </target> <!-- Generate the javadoc for the current Servlet API --> <target name="javadoc" depends="prepare-javadoc"> <javadoc sourcepath="${out.src.dir}" packagenames="org.apache.cactus.sample.*" destdir="${out.javadoc.dir}" author="true" public="true" version="true" use="true" windowtitle="${project.fullname} ${project.version} for Servlet @servlet.api@ API" doctitle="${project.fullname} ${project.version} for Servlet @servlet.api@ API" bottom="Copyright &copy; ${year} Apache Software Foundation. All Rights Reserved."> <classpath> <pathelement path="${java.class.path}"/> <pathelement location="${servlet.jar}"/> <pathelement location="${cactus.jar}"/> </classpath> </javadoc> </target> Step 9: 'doc' targetGenerate the project's documentation. It includes the javadoc, additional README files (if any) and the documentation web site Example: <!-- ======================================================================== Generate the full documentation ======================================================================== --> <!-- Preparation target for the doc target --> <target name="prepare-doc" depends="javadoc"> <mkdir dir="${out.doc.dir}"/> </target> <!-- Generate the documentation --> <target name="doc" depends="prepare-doc"> <!-- Create the zipped documentation --> <zip zipfile="${final.doc.name}" basedir="${out.doc.dir}"/> </target> Step 10: 'clean' targetRemoves all build generated files. <!-- ======================================================================== Remove all build generated files ======================================================================== --> <target name="clean" depends="init"> <!-- Deletes all files ending with '~' --> <delete> <fileset dir="." includes="**/*~" defaultexcludes="no"/> </delete> <!-- Remove the out directory --> <delete dir="${out.dir}"/> <!-- Delete log files --> <delete> <fileset dir="."> <include name="**/*.log"/> </fileset> </delete> </target> Step 11: 'jar', 'war' targets'jar' target
This target is useful if your project is a framework for example and
you need to deliver a jar file. We also include a manifest file
in the jar, with version information. We copy the manifest to the
output directory in order to replace the Example (from the Cactus build file): <!-- ======================================================================== Create the runtime jar file ======================================================================== --> <!-- Preparation target for the jar target --> <target name="prepare-jar" depends="compile"> <mkdir dir="${out.conf.dir}"/> <mkdir dir="${out.lib.dir}"/> <!-- Copy the manifest in order to replace the version token filter --> <copy todir="${out.conf.dir}" filtering="on"> <fileset dir="${conf.dir}" > <include name="manifest"/> </fileset> </copy> </target> <!-- Generate the jar file --> <target name="jar" depends="prepare-jar"> <jar jarfile="${final.jar.name}" basedir="${out.classes.dir}" manifest="${out.conf.dir}/manifest"> <!-- Do not include test files in the runtime jar --> <exclude name="**/Test*.*"/> <exclude name="**/test*.*"/> </jar> </target> 'war' target
This target is useful if you're building a web application.
We also include a manifest file in the war, with version
information. We copy the manifest to the output directory in order
to replace the Example (from the Cactus sample): <!-- ======================================================================== Create the runtime war file ======================================================================== --> <!-- Preparation target for the war target --> <target name="prepare-war" depends="compile"> <mkdir dir="${out.conf.dir}"/> <!-- Copy the manifest in order to replace the version token filter --> <copy todir="${out.conf.dir}" filtering="on"> <fileset dir="${conf.dir}" > <include name="manifest"/> </fileset> </copy> </target> <!-- Generate the war file --> <target name="war" depends="prepare-war"> <war warfile="${final.war.name}" webxml="${conf.dir}/web.xml" manifest="${out.conf.dir}/manifest"> <classes dir="${out.classes.dir}"> <!-- Do not include test files in the runtime jar --> <exclude name="**/Test*.*"/> <exclude name="**/test*.*"/> <!-- Also exclude the test cactus.properties file --> <exclude name="cactus.properties"/> </classes> <fileset dir="${web.dir}"> <exclude name="test/**"/> </fileset> </war> </target> Step 12: 'tests_XXX' target
The See the Using Cactus with Ant tutorial for details on to do this. |