http://www.developer.com/

Back to article

A Test-Driven Development Stack for Java: Maven, Jetty, Mockito, and JAX-RS


December 30, 2009

A colleague of mine told me a story that exemplifies the common attitude of non-technical personnel toward unit testing. He had done a presentation on test-driven development (TDD) for a new team he joined. Afterwards, the project manager gathered all the other developers in a separate room and told them flat out: "We do not have time to write unit tests, so just disregard everything he said."

Unfortunately, all too often this is the short-sighted attitude that developers face. From my own experience, every project where unit testing was not a priority has usually ended in delays and missed deadlines. Worst of all, every single major change to the system (which is inevitable) would bring up the spectre of breaking some existing functionality. Sooner or later, this would blow up during manual testing and send the whole team into a chaotic frenzy of last-minute patches, which led to an even more unstable system down the road.

Implementing TDD from the Start

Many believe TDD is expensive, but that is true only if your system was designed without any regard for either unit testing or the system's long-term maintenance costs. On a recent project, my team took those two issues into account right from the beginning by ensuring that all of our functionality was easily testable and by choosing the right tools (Maven and Jetty) to facilitate TDD for server-side applications.

The end result was that we finished our feature set weeks ahead of schedule, and we were able to implement any change that came along later with confidence. Any code breakage showed up in our unit test runs very quickly. We fixed any new defects reported by QA by first adding a unit test to reproduce it and then fixing the actual problem we found. As such, our latest TDD development project was one of the least stressful I've ever experienced. But it was the result of making the right choices right at the very beginning. TDD is not just something you can easily bolt on to an existing project.

This article provides an overview of how my team integrated different open source Java tools (Maven, Jetty, Mockito, and JAX-RS—with Spring to tie them all together) into one coherent and productive TDD stack. As my experience in the previously described project illustrates, the combination can be incredibly productive.

Maven Support for Unit Tests and Integration Tests

Early on, my team decided to move away from Ant (our previous build system) and fully embrace Maven as our build and software management tool. Besides obvious benefits (such as library dependency management), we were delighted to also discover how easy it is to run unit and integration tests; you just need run "mvn test" or "mvn integration-test" at any point (and unit tests run automatically if you package your application).

We found it was easiest to split our test code via test suites: one for regular unit tests and one for integration. Then we could configure our Maven pom.xml to run the appropriate unit tests, as shown here:

<plugin>
   <artifactId>
    maven
-
    surefire
-
    plugin
</artifactId> <configuration> <includes> <include>**/TestSuite.java</include> </includes> </configuration> <executions> <execution> <id>integration-tests</id> <phase>integration-test</phase> <goals> <goal>test</goal> </goals> <configuration> <skip>false</skip> <excludes> <exclude>none</exclude> </excludes> <includes> <include>**/IntegrationTestSuite.java</include> </includes> </configuration> </execution> </executions> </plugin>

Working with Maven in Eclipse

I've had very mixed experiences with the m2eclipse (i.e., the Maven for Eclipse) plugin. It seems great at first when it allows you to create new Maven projects or look for dependencies easily, but sooner or later it starts causing problems. Most often, my code stops compiling in Eclipse and starts showing errors (where there were none), but it will compile just fine if I run it via Maven from the command line.

In the end, I opted to just use regular Maven command-line functionality and invoke its Eclipse plugin (via "mvn eclipse:eclipse") to create and maintain the Eclipse project files. So, basically I use Eclipse for editing the code and Maven from command line to run it (if I ever change my pom.xml file).

If you want to debug your running code, this tutorial has instructions on how to set up Maven environment variables that will allow your Eclipse IDE to connect remotely to a running app.

Jetty Integration with the Maven Testing Lifecycle

I have become a huge fan of Jetty. Its fast startup and seamless integration with Maven give you a large productivity boost over traditional J2EE approaches (e.g., deploying the infamous EAR or WAR file). Embedded Jetty is capable of running directly off your code, without the need to create a WAR file. It also scans all of your Java classes automatically and restarts the context if any of them get changed (although that can sometimes cause PermGen errors, particularly with Spring and AOP classes). Nevertheless, it was far more productive than our previous J2EE/Ant/WebSphere stack.

From a TDD perspective, Maven perfectly integrates with Jetty by starting it up automatically before running integration tests and shutting it down as soon as the tests are finished. This makes running integration tests very smooth and easy.

Just add the Jetty plugin to your <build><plugins> section in pom.xml like this:

<plugin>
   <groupId>org.mortbay.jetty</groupId>
   <artifactId>
    maven
-
    jetty
-
    plugin
</artifactId> <version>6.1.20</version> <executions> <execution> <id>start-
    jetty
</id> <phase>
    pre
-integration-test</phase> <goals> <goal>run</goal> </goals> <configuration> <daemon>true</daemon> </configuration> </execution> <execution> <id>stop-
    jetty
</id> <phase>post-integration-test</phase> <goals> <goal>stop</goal> </goals> </execution> </executions> </plugin>

From that point on, you can run your web application using embedded Jetty by calling "mvn jetty:run".

Using Mock Objects for Unit Tests

We started off by using the established EasyMock library, but I soon discovered Mockito, the new mock object on the block. I was very impressed with its easy syntax and ability to seamlessly mock both interfaces and concrete classes. In a Spring-based application, this allows you to replace the injected dependencies in a component with mock versions that return hard-coded test values. We were able to test our service by mocking out the Hibernate DAOs it depends on—without actually running the app or being connected to any database. Here's an example:

PersonDao dao = mock(PersonDao.class);
   
Person p1 = new Person();
p1.setId(1);
p1.setFirstName("John");
p1.setLastName("Doe");

Person p2 = new Person();
p2.setId(2);
p2.setFirstName("Jane");
p2.setLastName("Doe");

List<Person> all = new LinkedList<Person>();
all.add(p1);
all.add(p2);

//return mocked result set on find
when(dao.findAll()).thenReturn(all);

//call the main method we want to test
List<Person> result = service.findAll();

//verify the method was called
verify(dao).findAll();

//verify correct result returned
assertEquals(2,result.size());

If you deal with a system that does not have easy entry points for integration testing, using mock objects may be your best solution.

To add Mockito to your project, just add this code in the <dependencies> section of your pom.xml:

<dependency>

   <groupId>org.mockito</groupId>

   <artifactId>mockito-all</artifactId>

   <version>1.8.0</version> 

</dependency>

Using JAX-RS (REST) for Integration Tests

My exposure to REST (and its Java implementation JAX-RS) has been fairly recent. However, once you grasp the benefits of REST, it's hard to imagine designing a server-side application without it. By exposing all your major functionality as REST services (with POST, GET, PUT and DELETE delivering the standard CRUD functionality), your whole system becomes wide open for thorough integration testing—not just from Java, but from any development language. Before settling on our Java unit testing stack, we contemplated performing integration testing via simple shell, Python, or Ruby scripts. However, Maven's seamless integration with JUnit made it a winner.

Most of our integration tests utilize JAX-RS client frameworks to perform POST, PUT and DELETE calls (to Create, Update, and Delete data), which we verify via GET (i.e., the "Read" part of CRUD) to ensure they are executed successfully. It's also very easy to test for negative conditions (e.g., invalid or missing data), as you can expect to receive a regular 500 server-side code error (in case of exceptions) or a 204 (No Content) error if an invalid search parameter was passed in a GET request.

JAX-RS (combined with JAXB annotated entities) allows you to serve JSON or XML payloads seamlessly, depending on what the calling client can accept (e.g., JSON for jQuery Ajax requests, XML for browser requests, etc.).

Here's an example of a JPA entity annotated with JAXB:

@Entity @XmlRootElement(name="Person") @XmlAccessorType(XmlAccessType.FIELD)
public class Person {

   @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
   @XmlElement private Integer id;
   @XmlElement private String firstName;
   @XmlElement private String lastName;
   @XmlElement private String email;

           ...getters/setters/etc....
}

Here is a Spring/JAX-RS REST web service to maintain it:

@Service("personService")
@Path(PersonService.PATH)
public class PersonService {
   
   public static final String PATH = "/person";

   private PersonDao dao;
   @Autowired  public void setPersonDao(PersonDao dao) {
      this.dao = dao;
   }
   
   @GET
   public List<Person> findAll() {
      List<Person> people = dao.findAll();
      return (people.size() > 0) ? people : null;
   }
   
   @GET @Path("{id}")
   public Person find(@PathParam("id") Integer id) {
      return dao.findByPrimaryKey(id);
   }

   @POST 
   public Response add(@FormParam("firstName") String firstName,
         @FormParam("lastName") String lastName, 
         @FormParam("email") String email) {
      
      Person person = new Person();
      person.setFirstName(firstName);
      person.setLastName(lastName);
      person.setEmail(email);
      Integer id = dao.save(person);
      
      return Response.created(UriBuilder.fromPath(PATH + "/{0}").build(id)).entity(person).build();
   }
   
   @PUT @Path("/{id}")
   public Response update(@PathParam("id") Integer id, 
         @FormParam("firstName") String firstName,
         @FormParam("lastName") String lastName, 
         @FormParam("email") String email) {
   
      Person person = dao.findByPrimaryKey(id);
      if (person != null) {
         if (firstName != null) {
            person.setFirstName(firstName);
         }
         if (lastName != null){
            person.setLastName(lastName);
         }
         if (email != null) {
            person.setEmail(email);
         }
         dao.update(person);
         return Response.ok(person).build();
      } else {
         return Response.noContent().build();
      }
      
   }
   
   @DELETE 
   public Response deleteAll() {
      dao.deleteAll();
      return Response.ok().build();
   }
   
   @DELETE @Path("/{id}")
   public Response delete(@PathParam("id") Integer id) {
      dao.deleteByPrimaryKey(id);
      return Response.ok().build();
   }
   
}

Code Coverage with Cobertura

Cobertura is a widely used tool for calculating code coverage (i.e., how much of your code is actually covered by your unit tests). The Cobertura Maven plugin makes it trivial to run Cobertura-instrumented code during unit tests (which is very complex to do by hand via Ant). It does not, however, support running during integration tests (yet). Supposedly, Cobertura will deliver support for this in an upcoming version of the plugin.

Code Samples

The accompanying code download for this article includes a simple Maven project that exposes a web service via REST, with examples of both unit tests and integration tests. Just execute mvn test to run unit tests and mvn integration-test to run integration tests. I hope it will serve as a useful blueprint for your future projects.

The TDD Recipe for Java

I hope this has been a useful overview of how you can integrate different open source Java tools into one coherent and productive development stack. The combination of Maven, Jetty, Mockito, and JAX-RS (with Spring to tie it all together) has been incredibly productive for my team, and we plan to use it as the basis of all our future projects.

As an added benefit, exposing all of your server-side functionality via REST (with JSON payloads) allows you to start creating your web UI using modern JavaScript client-side libraries such as jQuery UI, ExtJS, or JavaScriptMVC—thus, allowing you to stop using any Java server-side framework (such as Struts, JSF, or Spring MVC) altogether. But that's a topic for another article.

Code Download

  • TDD4Java.zip
  • About the Author

    Jacek Furmankiewicz is a Senior Java EE and Erlang/OTP Developer at Radialpoint. He has 15 years of professional IT experience and is a Sun Certified Java Programmer (SCJP).

    Sitemap | Contact Us

    Thanks for your registration, follow us on our social networks to keep up-to-date