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 { ... }
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.
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!
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.
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.
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:
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 String
s. XMLParameterizedRunner
supports the following value types:
String
Boolean
Integer
, Short
Long
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 List
s.
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.