Having written unit tests for the Java business logic classes, the next piece of the puzzle is to be able to run the tests from a command line build so that they can then be run on a build server or continuous integration server. Remember that these tests are pure logic tests and have no UI, also that they make use of RoboElectric to enable the tests to be run in a desktop JRE and do not require an emulator or device to be launched.

My development environment is Eclipse/Ant and I had already setup an Ant script to build release and debug configurations from the command line. I needed to extend the custom_rules.xml so that it would run the unit tests before building the release.

I added a CI target to do this

<!-- CI build -->
<target name="ci" depends="clean, prod, run_tests">
</target>

This is pretty much the same as a release/production build, the new piece being run_tests which is implemented like this

<!-- test project -->
<property name="app.project.libs.dir" value="libs/"/>
<property name="test.project.libs.dir" value="./tests/libs/"/>
<property name="test.project.build.dir" value="./tests/bin/"/>
<property name="test.project.source.dir" value="./tests/src/"/>
<property name="android.project.classpath" value="./bin/classes/"/>
<property name="test.report.dir" value="./test-reports/"/>
<property name="test.html.dir" value="./test-report-html/"/>

<!-- should I need to do this or is there a better way -->
<filelist id="app_jars" dir="${app.project.libs.dir}">
 <file name="slf4j-api-1.7.5.jar"/>
 <file name="roboguice-2.0.jar"/>
 <file name="javax.inject.jar"/>
 <file name="jsr305-1.3.9.jar"/>
 <file name="guice-3.0-no_aop.jar"/>
 <file name="android-support-v4.jar"/>
</filelist>

<filelist id="android_jars" dir="${test.project.libs.dir}">
 <file name="android.jar"/>
 <file name="maps.jar"/>
</filelist>

<!-- Note 
 we use slf4j-nop-1.7.5.jar as we do not want/need to log during unit tests 
 we need to reference android-support-v7-appcompat.jar
  as it is attached as a project reference in the main app
-->
<filelist id="libs_jars" dir="${test.project.libs.dir}">
 <file name="junit.jar"/>
 <file name="hamcrest.jar"/>
 <file name="robolectric-2.2-jar-with-dependencies.jar"/>
 <file name="android-support-v7-appcompat.jar"/>
 <file name="slf4j-nop-1.7.5.jar"/>
</filelist>

<path id="test_classpath">
 <pathelement path="${test.project.build.dir}"/>
 <pathelement path="${android.project.classpath}"/>
 <!-- junit.jar must come before android.jar! -->
 <filelist refid="libs_jars"/>
 <filelist refid="android_jars"/>
 <filelist refid="app_jars"/>
</path>

<target name="init_test">
 <tstamp/>
 <mkdir dir="${test.project.build.dir}"/>
</target>

<target name="compile_test" depends="init_test" description="compile test source">
 <javac srcdir="${test.project.source.dir}" destdir="${test.project.build.dir}"
     debug="true" includeantruntime="true">
  <classpath refid="test_classpath" />
 </javac>
</target>

<target name="test" depends="compile_test" description="Run JUnit tests">
 <mkdir dir="${test.report.dir}"/>
 <echo message="Running JUnit Tests in directory ${test.project.source.dir}..."/>
 <junit showoutput="true" printsummary="yes" failureproperty="junit.failure"
     fork="yes" forkmode="once" maxmemory="512m">
  <formatter type="plain"/>
  <formatter type="xml"/>
  <batchtest todir="${test.report.dir}">
   <fileset dir="${test.project.source.dir}">
    <include name="**/*Test.java"/>
    <include name="**/*Tests.java"/>
   </fileset>
  </batchtest>
  <classpath refid="test_classpath"/>
 </junit>
 <fail if="junit.failure" message="Unit test(s) failed. See reports in ${test.report.dir}"/>
</target>

<target name="clean_test" description="Clean Up" >
 <delete dir="${test.project.build.dir}"/>
 <delete dir="${test.report.dir}"/>
 <delete dir="${test.html.dir}"/>
 <delete file="${basedir}/tmp/cached-robolectric-classes.jar"/>
</target>

<target name="run_tests" depends="clean_test, test" description="Generate JUnit HTML reports">
 <mkdir dir="${test.html.dir}"/>
 <junitreport todir="${test.report.dir}">
  <fileset dir="${test.report.dir}" includes="TEST-*.xml"/>
  <report format="frames" todir="${test.html.dir}"/>
 </junitreport>  
</target>    

The properties at the start configure directory structure of the android project and the test project, in my case the structure looks like this.

test structure

And running the tests from the command line generates a report in XML and a rendered HTML version.

test results