September 23, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Test-Driven Development for Servlets

  • January 13, 2005
  • By Thomas Hammell
  • Send Email »
  • More Articles »

If you look at Listing 1, you can see that I used ServletUnit to write a test that calls the getOpenPool() method of the servlet to get the set of games for the football pool and then checks to make sure the pool returned is correct. You do this by first setting up ServletUnit's ServletRunner and ServletUnitClient and then using the InvocationContext object to execute one of the servlet's methods and check the results.

As you can see, writing the test first helped you make some decisions about the design of the servlet. You have decided that the servlet will have a getOpenPool() method that will take the request object as an argument and return a FootballPool. Also, as you can see, one of the nice features of ServletUnit is that it enables you to test the internal methods of a servlet. This allows you to do fine grain testing, which makes TDD easier.

At the moment, you cannot run the test because you have not created the actual servlet yet. Now that you understand the requirements of this method of the servlet, you can implement the servlet so that you can run the test (see Listing 2).

public class PlayerPickServlet extends HttpServlet
{
   public FootballPool getOpenPool(HttpServletRequest request)
   {
      PoolDatabase poolData =
         (PoolDatabase)request.getSession().getAttribute("PoolDatabase");
      FootballPool pool = poolData.getOpenPool();
      return(pool);
   }
}

Listing 2: getOpenPool method for the servlet

As you see, the implementation of the getOpenPool() method is pretty simple. You have made the assumption that the servlet will communicate with a database to get the data for the football pool. You have created a PoolDatabase interface call to define the methods the servlet will use to interact with the database (see the downloadable source code for details).

Now that you have written the servlet code, you now can run the test, which fails miserably. The problem is that when the servlet tries to get the PoolDatabase object, it gets a null pointer because it was never created. Creating a PoolDatabase object that the servlet can use could be a lot of work because it means creating an implementation of the PoolDatabase interface and hooking it up to a database. This could be a real hassle and make the test hard to run and maintain. Luckily, there is a better way to create a PoolDatabase object: by using a MockObject framework such as EasyMock. By using EasyMock, you can add some code to your test that will enable you to easily create a PoolDatabase object that the servlet can use. To do so, you add the following code to your test (updated code in bold).

public void testDisplayPlayerPicks()
   {
      MockControl poolDataMockcontrol;

      // Create a mock control to simulate the PoolDatabase class
      poolDataMockcontrol =
         MockControl.createControl(PoolDatabase.class);

      //Get mock object
      PoolDatabase mockPoolData = (PoolDatabase)
                                  poolDataMockcontrol.getMock();
      //Train the mock object
      mockPoolData.getOpenPool();
      //Create return value for mock method
      FootballPool fbPool = createFbPool();
      poolDataMockcontrol.setReturnValue(fbPool);
      poolDataMockcontrol.replay();
      // Set up servlet unit to run the PlayerPickServlet
      ServletRunner sr = new ServletRunner();
      sr.registerServlet("PlayerPickServlet",
                         PlayerPickServlet.class.getName());
      ServletUnitClient sc = sr.newClient();
      WebRequest request =
         new PostMethodWebRequest("http://localhost/PlayerPickServlet");
      try
      {
         // Use the InvocationContext to create and instance
         // of the servlet
         InvocationContext ic = sc.newInvocation(request);
         PlayerPickServlet ppickServlet = (PlayerPickServlet)
                                           ic.getServlet();
         assertNull("A session already exists",
                    ic.getRequest().getSession(false));
         HttpServletRequest ppickServletRequest = ic.getRequest();

         // Add the mock PoolDatabase class to the servlet's
         // session data
         HttpSession servletSession = ppickServletRequest.getSession();
         servletSession.setAttribute("PoolDatabase", mockPoolData);

         // Call the servlet's getOpenPool() method
         FootballPool openPool =
            ppickServlet.getOpenPool(ppickServletRequest);

         // Check the returned football pool to make sure it is
         // correct
         assertEquals("Kansas City", openPool.getAwayTeam(0));
         assertEquals("Green Bay", openPool.getHomeTeam(0));
         assertEquals("Houston", openPool.getAwayTeam(1));
         assertEquals("Tennessee", openPool.getHomeTeam(1));
         assertEquals("Carolina", openPool.getAwayTeam(2));
         assertEquals("Indianapolis", openPool.getHomeTeam(2));
         assertEquals("NY Giants", openPool.getAwayTeam(3));
         assertEquals("New England", openPool.getHomeTeam(3));
         assertEquals("Chicago", openPool.getAwayTeam(4));
         assertEquals("New Orleans", openPool.getHomeTeam(4));
         assertEquals("Oakland", openPool.getAwayTeam(5));
         assertEquals("Cleveland", openPool.getHomeTeam(5));
         assertEquals("Philadelphia", openPool.getAwayTeam(6));
         assertEquals("Dallas", openPool.getHomeTeam(6));
         assertEquals("Tampa Bay", openPool.getAwayTeam(7));
         assertEquals("Washington", openPool.getHomeTeam(7));
         assertEquals("Miami", openPool.getAwayTeam(8));
         assertEquals("Jacksonville", openPool.getHomeTeam(8));
         assertEquals("Pittsburgh", openPool.getAwayTeam(9));
         assertEquals("Denver", openPool.getHomeTeam(9));
         assertEquals("Buffalo", openPool.getAwayTeam(10));
         assertEquals("NY Jets", openPool.getHomeTeam(10));
         assertEquals("Baltimore", openPool.getAwayTeam(11));
         assertEquals("Arizona", openPool.getHomeTeam(11));
         assertEquals("San Francisco", openPool.getAwayTeam(12));
         assertEquals("Seattle", openPool.getHomeTeam(12));
         assertEquals("Atlanta", openPool.getAwayTeam(13));
         assertEquals("St. Louis", openPool.getHomeTeam(13));

         poolDataMockcontrol.verify();

      }
      catch (Exception e)
      {
         fail("Error testing DisplayPlayerPickServlet Exception
               is " + e);
         e.printStackTrace();
      }
   }

Listing 3: Updated Test for getting a list of the weekly football games

If you look at the updated code, you can see that you first used the EasyMock framework to create a mock PoolDatabase class; then you added this mock object to the servlet's session data. With this added code, you cannot run the test and get it to pass; this means that you are finished with the development for this method. To add other methods to the servlet, you would follow a similar pattern:

  • Create a simple test from the required behavior.
  • Add code to the servlet to get the test to compile and run.
  • Use a mock object to simulate the dependent classes needed by the servlet.
  • Refactor the test and source code to improve the code and fix any problems.

Developing servlets in this manner has many advantages, including providing a simple incremental way to develop code, having tests for all code, being able to refactor easily, ad so forth. Although it may take you a while to learn how to use tools such as Servlet Unit and EasyMock effectively, once you master the techniques shown here, you will be able to develop servlets faster with fewer bugs.

The zip file associated with this article, ServletTDD.zip, has a full set of source code for the example shown here. Experience is the best teacher, so I would encourage you to download and run the example to fully understand how to use TDD to develop servlets. I have also provided a list of reference to some TDD resources which should be useful. The list of resources includes a link to my book, Test-Driven Development: A J2EE Example, which focuses on explaining the tools and techniques needed to use TDD on a real J2EE project. It contains topics on servlets, JSP, and EJB development, as well as an explanation of how to use TDD to integrate all the pieces of a J2EE application. It's a good book with a lot of useful examples. I highly recommend it, but then again, I'm probably a little biased.

References

About the Author

Tom Hammell is a senior developer and currently works on the development of telecom network infrastruture software for the OpenCall Business Unit of Hewlett-Packard. Tom has been developing software for over 18 years and has worked on software in many different fields, such as satellite navigation, financial news wires, telecom, and J2EE application server development. Tom has published a number of articles on Java topics ranging from Swing development to Unit testing and recently wrote a book on Test-Driven Development. Tom holds a Bachelor of Science in Electrical Engineering degree and Masters of Computer Science degree from Steven's Institute of Technology. He may be reached at thomas_hammell@hp.com.





Page 2 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel