Written by Ivan Moore, Jester is an excellent tool that tests the unit tests that programmers and developers write. The tool is based on the assumption that there will be many areas in the code containing conditionals, loops, and case statements, as well as areas in which the cyclomatic complexity of the classes as a whole would suddenly spike or rise because multiple paths in execution are possible. Jester focuses on spots in the code like these. But to run, it requires a well-formatted classpath to the various resources.
Grester, which is an Apache Maven wrapper around Jester, alleviates the burden of dealing with the annoyances of constructing a Java™ classpath from project dependencies so you can test your execution points more easily, using Jester. Grester also tries to promote some of the benefits of using Maven, which is at the heart of its infrastructure. Jester would be extremely useful as an extra counter-check on code not written in a test-driven manner. Such code could be legacy code from older applications or even code more recently written by a group of developers who find Agile's test-driven ways a little too rough as an initial guide to building quality into code.
In fact, you can use Grester to expose the limitations of writing code in a nontest-driven manner. In my experience, scope creep and code that tends to miss or bypass what should be its true business function increase the bug count and the amount of zombie code (fast approaching the blob anti-pattern, even in small sections of code, not necessarily as a single hard-to-manage module or set of modules).
This article doesn't go into the technicalities of interpreting Jester's outputs and exactly how Jester works. For that information, see Resources for an excellent article written by Elliott Rusty Harold or visit Ivan Moore's Web site. This article is meant as a guide for acquiring and using the Maven plug-in wrapper around Jester.
You can get Grester from either of two sources, both of which are listed in Resources. The infrastructure needed to run it is pretty minimal: You need only Maven to build and use it. Grester is written in Groovy, a dynamic language that has Java-like syntax with the advantages of languages like Python and Ruby. At its heart, Grester is just another Maven plug-in for quickly running the Jester tool, so the true power of Grester comes from Jester. For this article, Jester V1.37 is used with the Grester V0.3 alpha release.
Jester without the groove: Why not?
If all project Java Archive (JAR) dependencies reside in a single location, running Jester directly couldn't be easier than referencing the single directory inside the Java classpath entry. However, when dependencies are spread out across a file system, the problem of configuration for each Jester run can be complicated and annoying, especially if the individual dependencies change location over time. With Maven, this process is eased dramatically.
Jester works in every instance outside a Maven project build configuration. So, what makes Grester so special? The answer lies in the manner in which Maven organizes its dependencies. Far from this "arrangement" being inefficient, Maven tries to standardize not only the manner in which Java (and, hence, Groovy) JARs and Web Archives (WARs) are found but also where they reside.
For those unfamiliar with Maven, the concept of a repository is used. There is a default local repository, located at $USER_HOME\.m2\repository, and a remote repository configured either in the pom.xml or the settings.xml file, located at $MAVEN_HOME/conf.
After you've tracked down the TAR-compressed resource (the .tar and tar.gz files are for UNIX® and Linux®) or the Microsoft® Windows® ZIP file, extract it. There are several ways to do this: Here, I use the Cygwin utility in Windows.
Figure 1. Extracting Grester with the Cygwin utility in Windows
You could also use the TAR utility, with the xzvf options
for tar.gz and the xvf options for the plain .tar file.
Figure 2 shows an example of this process.
Figure 2. Extracting the Grester tar.gz file with the TAR utility
The final directory structure should look like Figure 3.
Figure 3. Grester extracted on Windows
Configure, build, and install Grester
At this point, you're ready to make Maven aware of external repositories from which the relevant Grester Groovy dependencies can be acquired to compile and install Grester as a Maven plug-in locally. You do this by adding two remote repositories to the $MAVEN_HOME/conf/settings.xml file, as shown below.
Listing 1. Pointing Maven to remote repositories containing Groovy dependencies
<settings>
<profiles>
<profile>
<id>repositoryDefinitions</id>
<repositories>
.....
.....
<!-- You may have other repositories -->
....
....
<repository>
<id>apache-snapshotsv/id>
<name>Apache Snapshots Repository</name>
<url>http://people.apache.org/repo/m2-snapshot-repository</url>
<layout>default</layout>
<snapshots>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>ignore</checksumPolicy>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
.....
.....
</repositories>
...
</profile>
</profiles>
</settings>
|
Next comes the plug-in configuration for Maven, which specifies a repository for Grester's Groovy plug-in dependencies. This plug-in-repository configuration is placed within the same profile that was declared for the repositories (for example, the name repositoryDefinitions was used as the name of the profile), as shown below.
Listing 2. Pointing Maven to remote repositories containing Groovy plug-in dependencies
<settings>
<profiles>
<profile>
<id>repositoryDefinitions</id>
....
....
</repositories>
<pluginRepositories>
<!-- You may have other plug-in repositories -->
....
....
<pluginRepository>
<id>apache-snapshots</id>
<name>Apache Snapshots Repository</name>
<url>http://people.apache.org/repo/m2-snapshot-repository</url>
<layout>default</layout>
<snapshots>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>ignore</checksumPolicy>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
...
...
</pluginRepositories>
...
</profile>
</profiles>
</settings>
|
Now you can finally build the plug-in to completion. Grester requires Maven V2.0.5 or
later. If an earlier version is used, you will have problems compiling and using the
functionality in the Groovy-mojo-support dependency. When the $MAVEN_HOME/bin directory
is part of the system path of executable files, you execute the command mvn clean install from within the maven-grester-plugin directory
(the directory that contains Grester's pom.xml file), as shown below.
Figure 4. Building Grester from the command line
The build is usually fast (less than 20 seconds) to run. Figure 5 shows a successful installation window.
Figure 5. Installing Grester in Maven's local repository
It's important to note just where Grester is installed in Maven's local repository. If you are unfamiliar with Maven, its default local repository is $USER_HOME/.m2/repository/. On a computer running Windows, by default, $USER_HOME would most likely translate into Documents and Settings/$USERNAME/ (where $USERNAME is the logged-in user). On a Linux/UNIX machine, $USER_HOME would translate into /home/$USERNAME/. A quick look into a Windows local repository would show that Grester is installed in C:/Documents and Settings/$USERNAME/.m2/repository/org/apache/maven/plugins, and a directory named maven-grester-plugin is created. This directory contains the version number directory (the latest release is V0.3); within that directory is the actual maven-grester-plugin-x.x.jar file.
The reason for this structure lies in Grester's pom.xml file. As Figure 6 shows, the
groupId of the Grester project is org.apache.maven.plugins.
Any Maven plug-ins written in the Java or Groovy language that contain this string as
the value of the groupId contains mojos that are easier to
execute from the command line than Maven plug-ins that have some other arbitrary groupId. Because Grester uses this string, when executing individual
mojo goals from the command line, you don't need to prepend the groupId and artifactId.
Figure 6. Grester's
groupId in the pom.xml configuration file
The maven-grester-plugin directory is created upon installation (the install goal creates this directory), as shown below. Other standard
Maven plug-ins are installed in the same parent directory, such as the
maven-surefire-plugin and maven-install-plugin directories.
Figure 7. Grester in Maven's local repository
The use of the special groupId string is advantageous when
the custom group and artifact IDs for the project are either lengthy and hard to
remember or just cumbersome to type repeatedly. This is why the basic Maven goals from
its default plug-ins (for example, maven-compiler-plugin or maven-surefire-plugin),
such as compile, test, or even
test-compile, do not require a command like: mvn
org.apache.maven.plugin:maven-compiler-plugin:2.0.2:compile or mvn org.apache.maven.plugin:maven-surefire-plugin:2.3:test for their
execution (only mvn compile or mvn test).
Installing Jester as Grester's main dependency
At this point, you have everything in place except the actual Jester dependency, which
is the core of Grester. There are two convenience scripts from the Windows and
Linux/UNIX platforms that would install Jester (that is, the actual jester-1.37.jar
file) into Maven's local repository. Why are they provided? Couldn't you just download
them from the same external sources as Maven when it gets its compiler, installer, and
other plug-in dependencies? The answer is that Jester isn't out there on a publicly
available and known Maven repository (such as Ibiblio for Maven), so you can't
configure Maven's $MAVEN_HOME/conf/settings.xml file with a remote repository that
contained Jester (irrespective of the way it was installed with a groupId-artifactId-version combination).
Hence, the install-jester.bat and install-jester.sh executable files have been provided for Windows and Linux/UNIX, respectively. In case the execution of either one fails on either platform, you can use the command shown below as a fail-safe.
Figure 8. Installing the Jester dependency
Note: After I completed this article, Grester V1.0.1 was published to the publicly available Maven repository. This incremental improvement means that you can now obtain the plug-in directly from a well-known Apache repository, but the Jester core JAR and instructions for the combined use are still required to form a complete picture.
Using Grester in an example Maven project
So, you've got a nicely woven Maven project, and you'd like to test Jester on your unit tests (or at least on a group of tests). Regardless of whether the tests are unit or integration tests, it would be wise to either copy the project elsewhere in your file system and run Jester on that copy or use the existing copy but be prepared to revert any changes to your code source files. This is because Jester changes an existing source code file, saves the change and recompiles the code (leaving the classfile in the same directory as the source file). If the project has a relatively small code base or the tests chosen are few, the existing code base copy can be used.
Set up the example files in Eclipse
For a test example, you'll use a basic Maven project constructed and prepared in the Eclipse IDE. Just how you construct a Maven project and create the necessary file within a particular development environment is beyond the scope of this article, although the Resources section contains links and information on how to do it. Figure 9 illustrates the project inside the Eclipse IDE.
Figure 9. Example Maven project with the Eclipse IDE
As an example, use a relatively simple class and a test class to go along with it. The class deals with the execution of an external process command using the Java language. Listing 3 shows the main parts of the class under test.
Listing 3. Example class under test in the Maven project
package com.prometheus.run;
import java.io.IOException;
import java.io.InputStream;
public class CommandExecutor extends Executor{
...
public String executeCommand(String command){
...
try {
Process child = performCommandExecution(command);
stream = child.getInputStream();
sb = processStream(stream);
...
}
...
return sb.toString();
}
protected StringBuffer processStream(InputStream stream) throws IOException {
...
sb = new StringBuffer();
while ((c = stream.read()) != -1) {
sb.append((char)c);
}
return sb;
}
...
}
|
Within the CommandExecutor class, the executeCommand() method makes a call on a protected method within
the same class, processStream(). Within the processStream() method, a new StringBuffer instance is created and the InputStream manipulated within a while()
loop. Listing 4 shows the test class, again showing the main sections of the test.
Listing 4. Example test class in the Maven project
package com.prometheus.run;
import com.prometheus.run.CommandExecutor;
...
public class CommandExecutorTest extends TestCase {
...
public class MockProcess extends Process{
...
public InputStream getInputStream(){
String source= "This is a mock string";
return new ByteArrayInputStream(source.getBytes());
}
public OutputStream getOutputStream(){
return null;
}
public int waitFor(){
return 1;
}
}
public void testExecuteCommmand(){
String expected = "This is a mock string";
String actual = commandExecutor.executeCommand("lsmod");
assertEquals(expected, actual);
...
}
}
|
The test class, CommandExecutorTest, is relatively simple.
Although not too much detail has been given, the basic aim of this unit test is to mock
out the behavior of the Process class through the method
call performCommandExecution() from the class under test.
It is important to note that for Grester to run successfully, the project has to
compile both code sources and test sources and run any and all tests successfully.
(Note that because of this, the test-compile Maven phase
marks the phase from which a run of Grester is allowed and not before.) The next step
is simply to attach a Maven plug-in configuration for Grester in the project's pom.xml
file. This configuration is placed in the default build section of the pom.xml
file or within any legitimate Maven profile.
Listing 5 shows the example configuration of the Grester plug-in placed within the
example project's pom.xml file. Notice that the groupId
corresponds to org.apache.Maven.plugins and that the version is the latest Grester
plug-in: V0.3.
Listing 5. Grester plug-in configuration in the example project
<plugins>
...
...
<!-- START MAVEN GRESTER PLUG-IN CONFIGURATION -->
<plugin>
<groupId>org.apache.Maven.plugins</groupId>
<artifactId>Maven-Grester-plugin</artifactId>
<version>0.3</version>
<configuration>
<codeSources>src/main/java/com/prometheus/run</codeSources>
<testSuiteClass>com.prometheus.run.CommandExecutorTest</testSuiteClass>
</configuration>
<executions>
<execution>
<id>inspectSourcesCodeWithGrester</id>
<phase>test</phase>
<goals>
<goal>inspect</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- END MAVEN GRESTER PLUG-IN CONFIGURATION -->
...
</plugins>
|
Notice that the project has been set to run Grester's inspect goal during Maven's test phase. The codeSources point to a directory that contains the sources for which
the test class, CommandExecutorTest, exists. It could just
as easily have pointed to the actual class, CommandExecutor,
excluding the file name extension. In the README.txt file that comes with Grester, the
extension .Groovy is mentioned, but it should be noted that there is no current support
for using Grester against Groovy sources.
As of V0.3 alpha, Grester as a plug-in has two main goals (all lowercase when used), executable at any valid Maven life-cycle phase:
-
inspect— This is Grester's main goal, usually executed in the test phase (although strictly speaking, it can be any phase after the test-compile phase). Grester creates a viable Java classpath from the dependencies listed in the pom.xml file and feeds Jester the new classpath in turn. -
help— This goal, used mainly for references on the correct plug-in syntax and structure, can be executed in isolation on the command line asmvn grester:help.
Run Grester on the example project
Running the simple mvn clean install command (or any
life-cycle command that contains the specific phase that the inspect goal uses), would produce the output shown below.
Figure 10. Jester at work on the example code
Upon closer examination, you can see that line 27 in the original class file CommandExecutor has been changed from -1
to 1. It may take a while for Jester to perform a complete
operation on a single class. At the end of the operation, a jesterReport.xml file is
produced that shows summary details of what happened in the Java Swing window.
Running mvn grester:help from the command line
produces output similar to Figure 11. It serves as a simple short guide to configuring
Grester without referring to the original README.txt file.
Figure 11. Grester's help goal
Grester is not a perfect plug-in and is still being improved. Direct support for Groovy sources would be particularly helpful. The same idea be could apply to projects that don't use Maven yet require the construction of a Java classpath string across, for example, Apache Ant build files that list dependencies across multiple directories in a single file system. If the Ant files themselves have been split into many separate files, the process could be more difficult.
It is hard to say whether it's really worth going through the trouble of running a single tool (Jester) on projects whose dependencies cannot be readily identified easily in a single location, but it's also my impression that Jester is an important tool for testing the robustness of the way developers write tests — indeed, their very Test-Driven Development (TDD) and even Behaviour-Driven Development (BDD) skills are called into question when a Jester report shows poor unit- or integration-test performance for significant changes to a code base using a static set of tests.
Learn
-
Check out Ivan Moore's Jester site.
-
Read "Test your tests with
Jester," by Elliot Rusty Harold.
-
Read a snippet of Martin Fowler's "Test-Driven Development" comments on Jester.
-
Learn about Test-Driven
Development and how you can write more efficient tests with agility.
-
See this great information source on Behaviour-Driven Development and how TDD for the developer can be used correctly.
-
To listen to interesting interviews and discussions for software developers, check out developerWorks podcasts.
-
Stay current with developerWorks' Technical events and webcasts.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
Get products and technologies
-
Find Grester on
Freshmeat.net, the Open Source Project's Web site.
-
Find Grester on SourceForge.
-
Read about and use Groovy, a powerful dynamic
language that has Java language similarities.
-
Learn how Groovy
plug-ins for Maven are used to run and work with Groovy project sources.
-
The Apache Maven build tool has multiple advantages over Ant.
-
The publicly available Ibiblio Maven
Repository holds project dependencies declared as Maven artifacts.
-
Jester is mentioned in Kent Beck's book,
Test-Driven
Development (By Example)
.
-
Innovate your next open source development project with IBM trial software, available for download or on DVD.
-
Download IBM product evaluation versions, and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
-
Participate in developerWorks blogs and get involved in the developerWorks community.
Comments (Undergoing maintenance)






