Fabrizio Giudici is a Senior Java Architect with a long Java experience in the industrial field. He runs Tidalwave, his own consultancy company, and has contributed to Java success stories in a number of fields, including Formula One. Fabrizio often appears as a speaker at international Java conferences such as JavaOne and Devoxx and is member of JUG Milano and the NetBeans Dream Team. Fabrizio is a DZone MVB and is not an employee of DZone and has posted 67 posts at DZone. You can read more from them at their website. View Full User Profile

How to Use JUnit to Test your JavaFX Applications with NetBeans IDE

06.07.2009
| 15030 views |
  • submit to reddit
So, we have JavaFX 1.2 and upgraded support for NetBeans IDE 6.5.1. After a few hours of use, I can confirm that the programmer's experience is now really improved, with the editor much more supportive than before. What's missing of importance still? Simple answer: JUnit support.

In fact, strangely enough, there's no support for tests; not even a specific folder to put them in. A few people blogged about some tricks to run JUnit in a JavaFX project, treating JUnit as a simple library; but this approach didn't satisfy me, also because I want something that produces the regular reports (e.g., to be used by Hudson) and doesn't require to write too much extra code (such as explicitly creating TestSuites); in a word, something that will allow me to have all the things working just as they are when JUnit tests will be available in NetBeans IDE.

I was able to do that, at the price of applying two small patches to JUnit. Here it is how to do in six steps.

1. Create a new JavaFX project and put a dependency on your primary project

You need to do that because you don't want to put test sources together with regular sources. You have to create a Dummy.fx class with a void run() function and declare it as the main class in the project properties: NetBeans treats every project as it was a complete application and wants an entry point.

public function run(): Void
{
}

You need to put the JAR of JUnit as a library of your test project - but a patched version of JUnit (see below).

2. Create the tests

Keep the usual conventions, such as putting a test in the same package as the fixture; name the test with the ****Test pattern and test methods test***(). Since in JavaFX there are no annotations, you have to use the JUnit 3.x approach, that is you have to extend TestCase. I'm including a sample of a real test from blueBill Mobile (which just a few not relevant stuff omitted for brevity). Note that to assert the equality of two sequences I had to write a small ad hoc function, as sequences aren't array and aren't known to JUnit. Such code (and other similar) should probably packed in a specific, small library.

package it.tidalwave.bluebillmfx.taxon.controller;

import java.lang.System;
import it.tidalwave.bluebillmfx.taxon.model.Taxon;
import it.tidalwave.bluebillmfx.taxon.model.TaxonomyMock;
import it.tidalwave.bluebillmfx.taxon.model.TaxonomyImpl;
import junit.framework.TestCase;
import org.junit.Assert;

public class TaxonSearchControllerTest extends TestCase
{
def mockTaxonomy = TaxonomyMock{};
def fullTaxonomy = TaxonomyImpl{};

postinit
{
def is = getClass().getResourceAsStream("EBNItalia2003.json");
fullTaxonomy.load(is);
is.close();
}

def fixture = InstrumentedTaxonSearchController
{
taxons: mockTaxonomy.species;
}

def performanceFixture = InstrumentedTaxonSearchController
{
taxons: fullTaxonomy.species;
}

public function testFunction(): Void
{
assertFilter("", ["Airone cinerino", "Airone bianco maggiore", "Airone rosso", "Piro piro", "Piro piro boschereccio"], -1, "");
assertFilter("A", ["Airone cinerino", "Airone bianco maggiore", "Airone rosso"], -1, "Airone ");
...
}

function assertFilter (filter : String, expected : String[], expectedIndex : Integer, expectedLeading : String)
{
fixture.filter = filter;
assertEquals(expected, displayNames(fixture.filteredTaxons));
assertEquals("fixture.selectedTaxonIndex", expectedIndex, fixture.selectedTaxonIndex);
assertEquals("fixture.leading", expectedLeading, fixture.leading);
}

function assertEquals (expected : String[], actual : String[]) : Void
{
Assert.assertTrue("{actual}", expected == actual);
}
}

3. Create a specific Ant target for testing

It is pretty much copied from similar stuff in NetBeans, just patched for getting the right stuff in the classpath (this could be done in a better way, but it presently works).

    <target name="test" depends="init,compile">
<mkdir dir="${build.test.unit.results.dir}"/>
<junit showoutput="true" fork="true" failureproperty="tests.failed" errorproperty="tests.failed" filtertrace="${test.filter.trace}" tempdir="${build.test.unit.results.dir}">
<batchtest todir="${build.test.unit.results.dir}">
<fileset dir="${build.test.unit.classes.dir}">
<include name="**/*Test.class"/>
</fileset>
</batchtest>
<classpath>
<fileset dir="${platform.fxhome}/lib/shared">
<include name="*.jar"/>
</fileset>
<fileset dir="${platform.fxhome}/lib/desktop">
<include name="*.jar"/>
</fileset>
<pathelement path="${javac.classpath}"/>
<pathelement path="${build.classes.dir}"/>
<pathelement path="${build.test.unit.classes.dir}"/>
</classpath>
<!-- syspropertyset refid="test.unit.properties"/ -->
<jvmarg value="-ea"/>
<formatter type="brief" usefile="false"/>
<formatter type="xml"/>
</junit>
<fail if="tests.failed" unless="continue.after.failing.tests">Some tests failed; see details above.</fail>
</target>

4. Add a few properties in nbproject/project.properties

build.test.unit.classes.dir=${build.dir}/compiled
build.test.unit.results.dir=${build.dir}/test/results/

5. Patch JUnit

Now the most annoying part. Everything would already work, but for a detail: JUnit will complain about test classes having more than one constructor. Unfortunately, while JavaFX has got no constructors in the language, it creates synthetic code with two constructors in the bytecode (one with no parameters and one with a boolean parameter). I don't know what's it for, but for sure it makes JUnit complain. We have to patch JUnit. I downloaded JUnit 4.6 (I bet it's ok also 4.5) and patched these two files:

src/main/java/org/junit/runners/model/TestClass.java
src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java
diff TestClass.java TestClass.java.orig 
31,33c31,33
< // if (klass != null && klass.getConstructors().length > 1)
< // throw new IllegalArgumentException(
< // "Test class can only have one constructor");
---
> if (klass != null && klass.getConstructors().length > 1)
> throw new IllegalArgumentException(
> "Test class can only have one constructor");


diff BlockJUnit4ClassRunner.java BlockJUnit4ClassRunner.java.orig
118,121c118,121
< // if (!hasOneConstructor()) {
< // String gripe= "Test class should have exactly one public constructor";
< // errors.add(new Exception(gripe));
< // }
---
> if (!hasOneConstructor()) {
> String gripe= "Test class should have exactly one public constructor";
> errors.add(new Exception(gripe));
> }

This basically removes the enforcement of a single constructor.

I believe that JUnit is rather extensible and one can specify his own runner. Perhaps one could write some code and embed it together with tests, rather than patching the original, but unfortunately configuring this stuff requires annotations, and in JavaFX no annotations...

 

6. Create a small wrapper Ant script (for Hudson only)

Ok, at this time we have two NetBeans projects, the regular one and the one with tests. If you want to run this stuff under Hudson, put both of them into a directory and create this small script:

<?xml version="1.0" encoding="UTF-8"?>
<project name="blueBillMFX-global" default="hudson-fast" basedir=".">

<target name="hudson-fast">
<ant dir="blueBill-mobileFX" target="clean" inheritall="false"/>
<ant dir="blueBill-mobileFX" target="default" inheritall="false"/>
<ant dir="blueBill-mobileFXTest" target="clean" inheritall="false"/>
<ant dir="blueBill-mobileFXTest" target="test" inheritall="false"/>
</target>

</project>

Basically, the first call to ant might be redundant, as building the test project will make the first project to compile.

 

Ok, this can be surely improved, but in the meantime it just works, and you can start writing tests as they should be. The only important limitation it's that it won't work for a project configured with the Mobile profile, because of what sounds as a JavaFX compiler bug. My project is for the mobile profile, but I make sure that Hudson configures it as a desktop profile. I hope to fix this soon. Maybe in the meantime we will have NetBeans 6.7 final with JavaFX + JUnit support? Who knows.

What are you saying? Oh, you'd like to see an example project to try. Ok, blueBill Mobile isn't ready for primetime, but if you just want to look at how I configured tests you can check it out from Kenai:

svn co -r 141 https://kenai.com/svn/bluebill-mobile~svn/trunk/src/FX

 

Published at DZone with permission of Fabrizio Giudici, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

sub online replied on Fri, 2009/06/26 - 3:15am

Keep it down! You kids are too loud! Keep it down.links of londonYou're so great! You 'da bomb! You are the bomb.Tiffany JewelleryYou're asking for it. Asking for it.I knew it.Get off on the wrong foot.It's not gonna work.You'll pay for this.Where were we? It's hopeless.links of londonSee? I told you! It's cold outside. I should have worn my jacket. See? I told you. It's a cold day.How could you do this!How dare you stand me up.You're kidding right!

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.