JUnit, the unit test framework written by Erich Gamma and Kent Beck, is not the only alternative for unit testing in the Java environment. This article provides a simple demonstration on how to use Jython, PyUnit and Ant to unit test a Java project. To provide better elements of comparison, the example provided is the same as the one available in the JUnit distribution: MoneyTest.
This article assumes that the reader has some basic knowledge of unit testing, Java, Jython or Python and possibly Apache Ant. For more information about each of those technologies, see section Resources at the end of this article.
JyUnit, companion code for this article, is a rewrite of the unit test example provided with JUnit (MoneyTest). It also includes an Ant file (build.xml) to facilitate the integration and automation of Jython and the unit test process.
All the software components mentioned in this article are already available and heavily used.
In order to try JyUnit, you need:
File Name | Description |
build.xml | Ant project file |
java_test.bat | Windows batch to start the Java unit tests (JUnit) |
java_test.sh | Shell script to start the Java unit tests (JUnit) |
jython_test.bat | Windows batch to start the Jython unit tests (PyUnit) |
jython_test.sh | Shell script to launch the Jython unit tests (PyUnit) |
license.txt | License for JyUnit files |
MoneyTest.py | Re-write of the file MoneyTest.java in Python (Jython) |
readme.txt | Readme file |
The Jython registry file is located at the root of the installation directory of Jython. Set python.security.respectJavaAccessibility to false in the file registry, allows Jython scripts to gain access to non-public fields, methods and constructors of Java objects:
... # Setting this to false will allow Jython to provide access to # non-public fields, methods, and constructors of Java objects. #python.security.respectJavaAccessibility = true python.security.respectJavaAccessibility = false ...
This setting is not recommended for production applications, but is very useful in the context of unit tests. Without this setting, for each unit-test, you get the following error:
Traceback (most recent call last): File "MoneyTest.py", line 55, in setUp self.fMB1 = MoneyBag.create(self.f12CHF, self.f7USD) AttributeError: class 'junit.samples.money.MoneyBag' has no attribute 'create'
The class junit.samples.money.MoneyBag is not declared public, therefore the access is limited to classes from the same package, junit.samples.money in the example. This is not only limited to the test implementation in Python, this is a normal Java behavior. If the Java class MoneyTest did not belong to the same package as MoneyBag, we would obtain the following error when compiling MoneyTest.java:
MoneyTest.java:9: junit.samples.money.MoneyBag is not public in junit.samples.money; cannot be accessed from outside package import junit.samples.money.MoneyBag; ^
This demonstrates one interesting fact of using Jython to test Java application while keeping the testing code fully separated to the production code. With some work it is possible to obtain a similar behavior in Java, either by using the Java Reflection API, or some additional components for JUnit such as JUnit-addons or by simply having the test part of the same Java package (most commonly used).
After preparing the environment as explained above, you should be able to:
Perform the original JUnit test by launching java_test.bat or java_test.sh.
For more information related to Money and MoneyTest used to illustrate the unit tests examples, read the article JUnit Test Infected: Programmers Love Writing Tests, available from the JUnit web site.
The scripts java_test.bat or java_test.sh, allow to launch the unit tests provided with JUnit:
C:\junit3.8.1\jyunit>java_test ...................... Time: 0.03 OK (22 tests)
Jython allows to perform unit testing without dependency with JUnit because it includes PyUnit the Python unit test framework. You can verify the presence of the module unittest.py located in the subdirectory Lib of your Jython installation directory. The scripts jython_test.bat or jython_test.sh launch the Jython unit tests:
C:\junit3.8.1\jyunit>jython_test =================== Test without traces =================== ...................... ---------------------------------------------------------------------- Ran 22 tests in 0.040s OK ================ Test with traces ================ {[12 CHF][7 USD]} *2 == {[24 CHF][14 USD]} ... ok {[12 CHF][7 USD]} negate == {[-12 CHF][-7 USD]} ... ok testBagNotEquals (__main__.MoneyTest) ... ok {[12 CHF][7 USD]} + [14 CHF] == {[26 CHF][7 USD]} ... ok {[12 CHF][7 USD]} - {[14 CHF][21 USD] == {[-2 CHF][-14 USD]} ... ok {[12 CHF][7 USD]} + {[14 CHF][21 USD]} == {[26 CHF][28 USD]} ... ok testIsZero (__main__.MoneyTest) ... ok [12 CHF] + [7 USD] == {[12 CHF][7 USD]} ... ok testMoneyBagEquals (__main__.MoneyTest) ... ok testMoneyBagHash (__main__.MoneyTest) ... ok testMoneyEquals (__main__.MoneyTest) ... ok testMoneyHash (__main__.MoneyTest) ... ok {[12 CHF][7 USD]} - [12 CHF] == [7 USD] ... ok {[12 CHF][7 USD]} - {[12 CHF][3 USD]} == [4 USD] ... ok [12 CHF] - {[12 CHF][3 USD]} == [-3 USD] ... ok testPrint (__main__.MoneyTest) ... ok [12 CHF] + [14 CHF] == [26 CHF] ... ok [14 CHF] + {[12 CHF][7 USD]} == {[26 CHF][7 USD]} ... ok [14 CHF] *2 == [28 CHF] ... ok [14 CHF] negate == [-14 CHF] ... ok [14 CHF] - [12 CHF] == [2 CHF] ... ok testSimplify (__main__.MoneyTest) ... ok ---------------------------------------------------------------------- Ran 22 tests in 0.090s OK
The Jython tests without trace display the same information as JUnit and are executed with the command :
set CP=.;..;C:\jython\jython.jar java -cp %CP% org.python.util.jython MoneyTest.py
Note: The CLASSPATH with value .;..;C:\jython\jython.jar is just an example. It assumes that Jython is installed under Windows in the directory C:\jython and that the JyUnit package was extracted accordingly to the instructions above such that junit\samples\money can be accessed in the CLASSPATH with “..”.To launch the test with traces, execute the following command:
set CP=.;..;C:\jython\jython.jar java -cp %CP% org.python.util.jython MoneyTest.py -v
In this case, each method comment or docstring will be displayed. For example, for the test testBagSubtract:
1.
def
testBagSubtract(
self
):
2.
"""{[12 CHF][7 USD]} - {[14 CHF][21 USD] == {[-2 CHF][-14 USD]}"""
3.
expected
=
MoneyBag.create(Money(
-
2
,
"CHF"
), Money(
-
14
,
"USD"
))
4.
self
.assertEquals(expected,
self
.fMB1.subtract(
self
.fMB2))
PyUnit displays:
{[12 CHF][7 USD]} - {[14 CHF][21 USD] == {[-2 CHF][-14 USD]} ... ok
If there is no docstring, PyUnit displays the method name, its class and the result:
testBagNotEquals (__main__.MoneyTest) ... ok
The method testBagNotEquals is implemented in the file MoneyTest.py as follow:
1.
def
testBagNotEquals(
self
):
2.
bag
=
MoneyBag.create(
self
.f12CHF,
self
.f7USD)
3.
self
.assertFalse(bag.equals(Money(
12
,
"DEM"
).add(
self
.f7USD)))
In MoneyTest.py, in order to stay as close as possible to the reference implementation in Java (MoneyTest.java), the Java method comments are re-written under the form of docstring. Methods without comment in the Java file result in methods without docstring in MoneyTest.py.
The Ant file build.xml includes only the Jython unit tests. The tests are the same as the ones executed with the scripts. The build.xml example demonstrates two ways to easily integrate Jython in an Ant build file (without a Jython Ant task).
To launch the tests, assuming that the Ant binary files are in your PATH, at the command line, type:
C:\junit3.8.1\jyunit>ant Buildfile: build.xml init: test.call: jython.call: [java] ...................... [java] ------------------------------------------------------------ [java] Ran 22 tests in 0.060s [java] OK ...
The tests are performed four times:
AntCall is an Ant tasks part of the built-in Ant tasks. The version using MacroDef is more elegant but MacroDef is only available since Ant version 1.6.
Read the file build.xml provided with the sample source code to see usage examples of AntCall and MacroDef.
The Extreme Programming wave raising in the late nineties is phasing out a little. Nevertheless, some components of this development approach are well alive and evolving. In particular, the unit-tests, revealed by XP are now fully integrated in modern integrated development environments (Visual Studio, NetBeans, Eclipse…). You can find unit-test companion libraries for most of the programming languages and some languages even include unit-testing in their syntax (D Programming Language for example). The existing libraries have also been extended to address some specific issues and environments: HTTPUnit, XMLUnit…How valuable are Jython and PyUnit to test Java application knowing the large panel of unit-tests tools already available? PyUnit used with Jython does not offer an graphical user interface (GUI). There is no specific Ant task. The Java integrated development environments are mainly basing the unit testing on JUnit. At a first glance, Jython an PyUnit seem to appear more limited than JUnit to unit-test Java application.
But, despite those apparent weaknesses, Jython and PyUnit offer some interesting benefits:
Some points listed above are not limited to Jython and PyUnit. There are other serious candidates to unit test Java applications. Among the choices, you can combine Jython and JUnit, Groovy and JUnit, BeanShell and JUnit… Those are just some examples and each of those combinations could be the topic for a full separate article.
URL | Description |
http://www.jython.org/ | Jython |
http://www.python.org/ | Python |
http://www.junit.org/ | JUnit |
http://junit.sourceforge.net/ | JUnit |
http://pyunit.sourceforge.net/ | PyUnit |
http://ant.apache.org/ | Apache Ant |
http://java.sun.com/j2se/ | Java 2 SDK, Standard Edition |
http://sourceforge.net/projects/junit-addons/ | JUnit-addons |
http://www.eclipse.org/ | Eclipse |
http://www.netbeans.org/ | NetBeans |
http://jydt.sourceforge.net/ | Eclipse plugin: Jython Development Tools (JyDT) Eclipse |
http://coyote.dev.java.net/ | Coyote: Dynamic/scripting language support in NetBeans |
http://groovy.codehaus.org/ | Groovy |
http://www.beanshell.org/ | BeanShell |