Skip to main content

Make JUnit testing Java applications easier with Grester

Grester is an Apache Maven wrapper around Jester that checks code not written in a test-driven manner

Michael Nyika (mnyika@gmail.com), Senior Software Developer, Stelligent Incorporated
Michael Nyika
Michael Nyika is a J2EE consultant in the northern Virginia area. He has been developing software on both the Java and Microsoft .NET platforms for seven years and is a Linux/SELinux enthusiast.

Summary:  So, you've written a bunch of unit tests. As a developer, you run your tests multiple times per day, especially in a continuous integration environment. But how badly would they break if the sources had to change? When Jester and Maven combine to make Grester, you can quickly find out.

Date:  29 Apr 2008
Level:  Intermediate
Activity:  1720 views

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.

Getting Grester

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.

Note for Linux and UNIX users

Grester's file size is small, and when you've extracted it, you can safely delete the actual compressed archive. Cygwin was used here on a Windows machine to show how simple it is to extract it even in a simulated Linux environment. It is not recommended that Grester versions earlier than 0.3 be experimented with on Linux or UNIX systems, however, because of some missing operating system features, although the Windows alpha versions are pretty stable. In all versions, however, Jester V1.37 was used.

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.


Installing Grester

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
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
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
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
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
Installing Grester in Maven's local repository

Grester's TDD approach

When writing Grester, I had to write the integration tests while keeping in mind the operating system environment they would be written for. This came to be a bit challenging because I'd start off with if . . . else clauses that matched the operating system type, then made an assertion based on the type. After a while, the realization that a stable, successful build across two platforms could be achieved if tests were written for a platform while writing on that platform and not before.

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
Grester pom.xml groupId

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
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
Jester dependency installation

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
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.

Wire Grester into the project

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 as mvn 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
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.

Asking for Grester help

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's help goal


Conclusion

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.


Resources

Learn

Get products and technologies

Discuss

About the author

Michael Nyika

Michael Nyika is a J2EE consultant in the northern Virginia area. He has been developing software on both the Java and Microsoft .NET platforms for seven years and is a Linux/SELinux enthusiast.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=304399
ArticleTitle=Make JUnit testing Java applications easier with Grester
publish-date=04292008
author1-email=mnyika@gmail.com
author1-email-cc=cappel@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers