Mastodon

JUnit tests with Ant and Hudson

One of my more persistent current problems is the setup of JUnit tests in our Hudson 3.1.1. After days of try and error, I still couldn’t manage to make it work. This article documents my experiments and will hopefully be updated with the solution one day. If you, the reader of this article, came up with a solution, please contact me or leave a comment. Thanks! Please note that the source/xml listings are prototypic.

The first obvious steps are the setup of the hudson job.

JUnit Test With Ant And Hudson 1

The parameter “-v” enables verbose logging and provides much more information about what’s going on and what errors occur. The ant file from the screenshot is this:

<?xml version="1.0" encoding="UTF-8"?>
<project name="server.test" default="test" basedir=".">
	<import file="build.xml" />

	<property name="src" value="src" />
	<property name="test.reports" value="junit_output" />
	<path id="classPathJUnit">
 
		<!This will add all of our jars into the classpath -->
		<fileset dir="../target/target/apf/plugins">
			<include name="**/*.jar"/>
		</fileset>
 
		<!This will add all of our test classes into the classpath -->
		<pathelement path="../server.test/@dot/middleware/" />
	</path>
 
	<target name="test" depends="build.jars" description="JUnit-Tests durchführen">
		<delete dir="${test.reports}" />
		<mkdir dir="${test.reports}" />
 
		<echo message="Classpath: ${toString:classPathJUnit}"/>
 
		<junit printsummary="yes" fork="yes" haltonfailure="no">
			<classpath refid="classPathJUnit" />
			<formatter type="xml" />
 
			<batchtest todir="${test.reports}">
				<fileset dir="${src}">
					<include name="**/*Test.java" />
				</fileset>
			</batchtest>
		</junit>
		<echo message="Ready – test results in ${test.reports}" />
	</target>
</project>

Notice the “toString:classPathJUnit”. That will print the classpath used for our tests. As you can see, the resulting test files are placed in /junit_output. To display the results, Hudson needs to be pointed to this directory:

JUnit Test With Ant And Hudson 2

Note that the directory itself is referenced! I’ve seen configurations like “*/Test.xml”, with which our Hudson couldn’t find a single result file. Running this configuration results in:

[junit] Running middleware.DummyTest
[junit] Tests run: 1, Failures: 0, Errors: 1, Time elapsed: 0 sec

BUILD FAILED
D:\Programme\Hudson\jobs\steven_junit_fuer_hudson\workspace\steven_junit_fuer_hudson\server.test\junitTests.xml:37: Process fork failed.
[…]
Caused by: java.io.IOException: Cannot run program "D:\CI\jdk1.7.0_21_x86\jre\bin\java.exe": CreateProcess error=206, The filename or extension is too long
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1042)
at java.lang.Runtime.exec(Runtime.java:615)
at org.apache.tools.ant.taskdefs.Execute$Java13CommandLauncher.exec(Execute.java:862)
at org.apache.tools.ant.taskdefs.Execute.launch(Execute.java:481)
at org.apache.tools.ant.taskdefs.Execute.execute(Execute.java:495)
at org.apache.tools.ant.taskdefs.optional.junit.JUnitTask.executeAsForked(JUnitTask.java:1061)
... 19 more
Caused by: java.io.IOException: CreateProcess error=206, The filename or extension is too long
at java.lang.ProcessImpl.create(Native Method)
at java.lang.ProcessImpl.<init>(ProcessImpl.java:288)
at java.lang.ProcessImpl.start(ProcessImpl.java:133)
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1023)
... 24 more

Total time: 17 seconds
Zeichne Testergebnisse auf.
Finished: FAILURE

The reason for this failure is the huge classpath in combination with fork=”yes” in our build xml. Because I added every one of our library jars, the forked virtual machine cannot be instantiated. So what happens if we remove the fork=”yes” in our build xml?

[junit] Running middleware.DummyTest
[junit] Tests run: 1, Failures: 0, Errors: 1, Time elapsed: 0 sec
[junit] Test middleware.DummyTest FAILED
[…]
[echo] Ready - test results in junit_output

BUILD SUCCESSFUL
Total time: 4 seconds
Zeichne Testergebnisse auf.
Testergebnisberichte enthalten keine Testergebnisse.
Finished: FAILURE

The reason for the failure of the test can be seen in the resulting xml file. In the Hudson workspace, the folder junit_output has been created and there’s a DummyTest.xml which reads:

<error message="middleware.DummyTest" type="java.lang.ClassNotFoundException">java.lang.ClassNotFoundException: middleware.DummyTest
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:266)
</error>

Because the compiled test class couldn’t be found, no tests have been executed. Hence, the result file doesn’t have any results. Out of despair I additionally added each test class to the classpath using a filesest:

<?xml version="1.0" encoding="UTF-8"?>
<project name="server.test" default="test" basedir=".">
	<import file="build.xml" />

	<property name="src" value="src" />
	<property name="test.reports" value="junit_output" />
	<path id="classPathJUnit">
		<fileset dir="../target/plugins">
			<include name="**/*.jar"/>
		</fileset>
		<pathelement path="../server.test/@dot/middleware/" />
		<fileset dir="../server.test/@dot/middleware/">
			<include name="**/*.class"/>
		</fileset>
	</path>
 
	<target name="test" depends="build.jars" description="JUnit-Tests durchführen">
		<delete dir="${test.reports}" />
		<mkdir dir="${test.reports}" />
 
		<echo message="Classpath: ${toString:classPathJUnit}"/>
 
		<junit printsummary="yes" haltonfailure="no">
			<classpath refid="classPathJUnit" />
			<formatter type="xml" />
 
			<batchtest todir="${test.reports}">
				<fileset dir="${src}">
					<include name="**/*Test.java" />
				</fileset>
			</batchtest>
		</junit>
		<echo message="Ready - test results in ${test.reports}" />
	</target>
</project>

Now, the class files could be found, but not read:

Ignoring Exception java.util.zip.ZipException: error in opening zip file reading resource middleware/DummyTest.class from D:\Programme\Hudson\jobs\steven_junit_fuer_hudson\workspace\steven_junit_fuer_hudson\server.test\@dot\middleware\DummyTest.class
[…]
[echo] Ready - test results in junit_output

BUILD SUCCESSFUL
Total time: 8 seconds
Zeichne Testergebnisse auf.
Testergebnisberichte enthalten keine Testergebnisse.
Finished: FAILURE

This error is described in an article by Anthony Chaves. Because JUnit expects the test classes to be in a jar file, Anthony changed his build script to build a separate jar for the tests. I don’t want to change our build process in this way, so I stopped searching for a solution to bring Hudson, Ant and JUnit together. Instead, I will try to get the tests running in Jenkins. I will update this article in doing so. Again: If you can help me setup JUnit on Hudson, please contact me! Thanks!

Update:With the help of a coworker, we figured out that the solution to the problem above was indeed to build a separate jar for the tests. We now have two jobs on Hudson: One to build and one to test. That works just fine.