SourceForge.net Logo

Tutorial

How to define prerequisites

It happens often, that a test case requires some prerequisites, e.g. be online in internet, database is available, database is filled with required test data. In this case, the test cases would fail. To use the @Igore is not applicable, if I am always switching between such configurations.

The required approach would be to check this during running tests. So you can simply add a @Prerequisite(requires="databaseIsAvailable" annotation.

public class TestFillDatabase {
    @Prerequisite(requires = "databaseIsAvailable")
    @Test public void fillData() {
        // ...
    }
    
    public boolean databaseIsAvailable() {
        boolean isAvailable = ...;
        return isAvailable;
    }
}
public class TestFillDatabase {
	@Prerequisite(requires = "databaseIsAvailable")
	@Test public void fillData() {
		// ...
	}
	public boolean databaseIsAvailable() {
		boolean isAvailable = ...;
		return isAvailable ;
	}
}

This specified methods with @Prerequisite(requires = "databaseIsAvailable") must be a public method, returning a boolean or Boolean value.

If these methods will be consolidated in helper classes, you can also specify static methods within a class to be called using @Prerequisite(requires = "databaseIsAvailable", callee="DBHelper").

public class TestFillDatabase {
	@Prerequisite(requires = "databaseIsAvailable", callee="DBHelper")
	@Test public void fillData() {
		// ...
	}
}
public class DBHelper {
	public static boolean databaseIsAvailable() {
		boolean isAvailable = ...;
		return isAvailable ;
	}
}

Since JUnitExt 0.2.1, the called prerequisite method can also be implemented with following signatures (lookup with this ordering):

  public boolean somePrereqMethod (Description desc);
  public boolean somePrereqMethod (String className, String methodName);
  public boolean somePrereqMethod ();
This will allow for dynamic logic based on test description or class and method name. See also org.junitext.samples.SimpleTest in distribution.

To run these tests you have to specify to use a specific runner:

@RunWith(AnnotationRunner.class)
public class TestFillDatabase {
	...
}

How to use categories

Tests can be categorized, to sort them for different puporses. JUnitExt provides a CategoryTextListerner, which prints out at end of test run all categories, with status of tests listed: May be Success, Ignored, Failed. You can plugin the category text listener using following code: (see also org.junitext.samples package)

	JUnitCore core = new JUnitCore();
	// use for categories special listener, give some statistics
	core.addListener(new CategoryTextListener(System.out));
	core.run(SimpleTest.class);

The output when running thes tests will be:

I.
Time: 0

OK (1 test)

Category: equal tests
  Success testEquals(org.junitext.samples.SimpleTest)
Category: math tests
  Ignored divideByZero(org.junitext.samples.SimpleTest)

A further integration withan IDE (e.g. Eclipse) is planned.

How to parameterize tests using XML

It is sometimes desirable to separate the test data from the tests themselves. There are two main reasons for doing so:

To address these types of testing needs, JUnit Extensions contains a custom test runner that can parameterize tests using data contained in an XML file. The XML input file format attempts to mimic the Spring XML bean declaration format, as many developers are familiar with the format.

JUnit 4 has a ParameterizedRunner test runner, however, it gets the test data from a static method defined on the test class itself. XMLParameterizedRunner is similar in behavior to the ParameterizedRunner, with the key exception that the test data resides in an XML file.

Okay, onward with some examples. This is a tutorial, after all!

Defining the test class

When defining a test class that will use the XMLParameterizedRunner, the test class needs the @RunWith attribute specifying that the XMLParameterizedRunner should be used. In addition, the @XMLParameters annotation needs to be added to tell the XMLParameterizedRunner the name of the data XML file. Below is an example class demonstrating the use of the two annotations:

@RunWith(XMLParameterizedRunner.class)
public class XMLParameterizedSampleTest {
	private String expectedName;

	private String actualName;

	@XMLParameters("/org/junitext/samples/Data.xml")
	public XMLParameterizedSampleTest(String expected, String actual) {
		this.expectedName = expected;
		this.actualName = actual;
	}

	@Test
	public void namesAreEqual() {
		assertEquals("The expected name does not equal the actual name.",
				expectedName, actualName);
	}
}

The XMLParamterizedRunner will search the classpath for the data XML file. In the example above, it will look for the "Data.xml" file. All of the standard rules for loading resources from the classpath apply, so something like "org/junitext/samples/Data.xml" is also completely legal.

Also note that the @XMLParameters annotation must be applied to the test class's constructor. If there are multiple @XMLParameters annotations, then XMLParamterizedRunner will only use the first one.

Defining the test data

Below is the "Data.xml" for the above example test class:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<tests>
  <test>
     <string id="expected" value="Daneel Olivaw" />
     <string id="expected" value="Daneel Olivaw" />
  </test>
  <test>
     <string id="expected" value="Daneel Olivaw" />
     <string id="expected" value="Johnny 5" />
  </test>
</tests>

For each <test> element encountered, a new instance of the test class will be created using the parameters defined within the body of the <test> element. Consequently, each test method contained in the test class will be invoked once per <test> element.

Running the test

Below is the output if you were to run the above test using the standard JUnit 4 test runner:

JUnit version 4.2
..E
Time: 0.03
There was 1 failure:
1) testRobotData[1](org.junitext.samples.XMLParameterizedSampleTest)
org.junit.ComparisonFailure: The expected name does not equal the actual name. expected:<[Daneel Olivaw]> but was:<[Johnny 5]>
	at org.junit.Assert.assertEquals(Assert.java:94)
	at org.junitext.samples.XMLParameterizedSampleTest.testRobotData(XMLParameterizedSampleTest.java:27)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99)
	at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81)
	at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
	at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75)
	at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45)
	at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:75)
	at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:36)
	at org.junit.internal.runners.CompositeRunner.run(CompositeRunner.java:29)
	at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42)
	at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
	at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52)
	at org.junit.internal.runners.CompositeRunner.run(CompositeRunner.java:29)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:130)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:109)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:100)
	at org.junit.runner.JUnitCore.runMain(JUnitCore.java:81)
	at org.junit.runner.JUnitCore.main(JUnitCore.java:44)

FAILURES!!!
Tests run: 2,  Failures: 1

You will notice several things from the test run output:

Just like the JUnit 4 ParamterizedRunner, the XMLParameterizedRunner appends an index to each parameterized test so that it is easier to identify the different test runs from each other. In a future version of the XMLParameterizedRunner it will be possible to specify a unique label for each test run in the XML file.

Thanks to the excellent architecture of JUnit 4, XMLParamterizedRunner works perfectly with any JUnit test runner. Here is what the test output looks like when the example test is run from within Eclipse:

Of course, XMLParameterizedRunner would be pretty limited if all it could handle were Strings. XMLParameterizedRunner supports the following value types:

Supported parameter types

In addition to basic value types, XMLParameterizedRunner supports the creation of complex Java objects that conform to the JavaBeans standard. Beans are declared in very much the same way that they are in Spring:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<tests>
  <test>
    <bean id="robot" class="org.junitext.samples.Robot" >
      <property name="name" value="Daneel Olivaw" />
      <property name="id" value="134" />
      <property name="model" value="X24R" />
      <property name="manufacturer" value="Han Fastolfe" />
      <property name="friends">
        <list>
            <bean id="friend" class="org.junitext.samples.Robot" >
              <property name="name" value="Johnny 5" />
              <property name="id" value="5" />
              <property name="model" value="SAINT" />
              <property name="manufacturer" value="Nova Laboratories" />            
          </bean>
        </list>
      </property>
    </bean>
  </test>
</tests>

As you can see, XMLParameterizedRunner even supports setting bean properties that contain Lists.

It is important to note, however, that the bean declaration support in the current version of XMLParameterizedRunner is still very rudimentary. Support for more advanced bean declarations is planned for future versions.