ant based testing for openmrs

So I needed to do some module development on OpenMRS and I didn’t want to setup eclipse. I had already figured out the basics of gabbing the code, building, and deploying things… but testing was missing. So I spent some time playing with JUnit ant tasks. In the end I came up with a single build.xml file that will work for core and for modules:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?xml version="1.0"?>
<!-- ********************************************************* -->
<!-- ** Tests                                               ** -->
<!-- **                                                     ** -->
<!-- ** Some options:                                       ** -->
<!-- **                                                     ** -->
<!-- **   ant test -Dtest=org.openmrs.YourClassTest         ** -->
<!-- **   ant test -Dformat=(plain|brief|xml)               ** -->
<!-- **                                                     ** -->
<!-- ** @author Jeff Rafter                                 ** -->
<!-- ** @version 0.1                                        ** -->
<!-- ********************************************************* -->
<project name="test" default="test">    
  <property name="fork" value="yes" />
  <property name="base.dir" location=".." />
  
  <condition property="format.plain">
    <matches string="${format}" pattern="plain" />
  </condition>
  
  <condition property="format.brief">
    <matches string="${format}" pattern="brief" />
  </condition>
  
  <condition property="format.xml">
    <matches string="${format}" pattern="xml" />
  </condition>
  
  <path id="classpath">
    <pathelement location="api"/>
    <pathelement location="web"/>
    <pathelement location="build"/>
    <pathelement location="${base.dir}/build"/>
    <pathelement location="${base.dir}/dist"/>
    <pathelement location="${base.dir}/web/WEB-INF"/>
    <pathelement location="${base.dir}/web/META-INF"/>
    <!-- This might be a little greedy -->
    <fileset dir="${base.dir}">
      <include name="**/*.jar" />
    </fileset>
  </path>    

  <target name="test">
    <!-- Clean out the old test class build area -->
    <delete dir="build" />

    <!-- Setup the new test target -->
    <mkdir dir="build" />

    <!-- Copy over the log4j.xml so that it is used -->
    <copy todir="build/">
      <fileset dir="metadata/" includes="**/*" />
    </copy>

    <!-- Compile the tests -->
    <javac destdir="build" debug="true" debuglevel="lines,source,vars">
      <classpath refid="classpath" />
      <src path="." />
      <include name="**/*.java" />
    </javac>

    <!-- Run the units -->
    <junit printsummary="true" haltonfailure="false" failureProperty="test.failure">
      <formatter type="plain" usefile="false" if="format.plain"/>
      <formatter type="brief" usefile="false" if="format.brief"/>
      <formatter type="xml" usefile="false" if="format.xml"/>
      <classpath refid="classpath" />
      <test name="${test}" if="test"/> 
      <batchtest fork="${fork}" unless="test">
        <fileset dir="build">
          <include name="**/*Test.class"/>
          <exclude name="**/*BaseTest*" />
          <exclude name="**/Base*" />          
          <exclude name="**/OpenmrsTestsTest*" />
        </fileset>
      </batchtest>
    </junit>
    
    <!-- Fail the ant build if a test failed -->
    <fail message="test failed" if="test.failure" />
  </target>
</project>

I put this file in the test folder in the repository. This means that it is separate from the main build.xml which I felt was a bit safer for now. To make the output behave, you need to create a separate log4j.xml to silence a lot of the output. Create a metadata folder inside of your test folder and make copy the following content into log4j.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" 
        value="%p - %C{1}.%M(%L) |%d{ISO8601}| %m%n" />
    </layout>
  </appender>
  <root> 
    <priority value="FATAL" /> 
    <appender-ref ref="CONSOLE" /> 
  </root>
</log4j:configuration>

If, for some reason, you need the logger output, simply change the FATAL value to DEBUG or WARN.

Also take note that I have added a couple of options to the main task:

1
2
<!-- **   ant test -Dtest=org.openmrs.YourClassTest         ** -->
<!-- **   ant test -Dformat=(plain|brief|xml)               ** -->

Passing in the test property allows you to run only the tests in a single test class. It still recompiles all of the tests, but that doesn’t seem to take too long. JUnit allows multiple formatters, but by default I am not using them. If you want to see more verbose output set the format property.

One last option:

1
    <property name="fork" value="yes" />

I have broken out the option to fork when running the tests. By default the test runner will fork a new JVM process for every test class. Turning this off makes the tests run much much more quickly. In module tests this will generally work great, however, turning this off might run you out of memory, and breaks the core tests. To turn it off, simply change the value to no.