Unit Test Your Struts Application

Unit Test Your Struts Application Unit Test Your Struts Application

by Lu Jian
09/22/2004

Unit testing is an important part of a good development process. Although there are many unit testing frameworks and technologies, such as JUnit, Cactus, EasyMock, HttpUnit, and so on, developers often find that it is difficult to unit test their Struts applications.

This article introduces StrutsUT, a simple extension to the Cactus framework, to help solve this problem. It provides two solutions to unit test Struts applications: a "traditional" solution and one based on AspectJ. Developers can choose either for their convenience.

This article explains the initial idea of Cactus from a developer's point of view and extends this idea further into the Struts domain, which is the core of StrutsUT. The reader should have some knowledge of and experience in Struts framework, JUnit, Cactus, and/or AspectJ.

An Overview of JUnit and Cactus

What is JUnit?

JUnit is a framework to create and perform unit tests on Java classes. With the help of mock objects and override technology, JUnit can perform unit tests for most Java applications. See References below to learn more about JUnit and mock objects. In this article, I chose EasyMock as the mock object implementation.

Below is a simple Java class with its test case.

//SimpleClass.java

package unittest.simple;



public class SimpleClass {

    public SimpleClass() {

    }

    

    public String foo(int n) {

        ExternalInf inf = getExternalInf();

        String s = inf.doSomeExtThing(n);

        return "Woo!" + s;

    }

    

    protected ExternalInf getExternalInf() {

        ExternalInf inf = null;

        

        //do some operation to get the interface

        //JNDI call or something else

        return inf;

    }

}



//ExternalInf.java



package unittest.simple;



public interface ExternalInf {

    //return "Great" when n equals 10

    String doSomeExtThing(int n);

} 



//SimpleClassTest.java



package unittest.simple;



import org.easymock.MockControl;



import junit.framework.TestCase;



public class SimpleClassTest extends TestCase {

   protected void setUp() throws Exception {

      super.setUp();

   }



   protected void tearDown() throws Exception {

      super.tearDown();

   }



   //Test foo() method in SimpleClass

   public void testFoo() {

      //define the mock object

      MockControl controller = MockControl.

           createControl(ExternalInf.class);

      final ExternalInf inf = (ExternalInf)

           controller.getMock();

       

      //define the behavior of mock object

      inf.doSomeExtThing(10);

      controller.setReturnValue("Great");

      controller.replay();

        

      //use override technology to bridge from 

      //mock object to the class to be tested

      SimpleClass instance = new SimpleClass() {

         protected ExternalInf getExternalInf() {

            return inf;

         }

      };

        

      //start test

      String result = instance.foo(10);

        

      //do verification between expected result 

      //and actual result

      assertEquals("Woo!Great", result);

        

      //do verification on the mock object

      controller.verify();

   }

}

In the example above, we use a mock object to simulate the external interface and override the getExternalInf method to bridge the mock object to the real class to be tested. It this article, I call these two technologies "traditional unit-test technologies".

The Problems

When we look at a web application, we find it is still possible to use mock objects and override technology to do the unit test. But this approach has several limitations:

  • It involves a large workload.

    We need to mock up the HttpServletRequest, HttpServletResponse, and Session objects. This is a tedious and error-prone task.

  • It is difficult to compare the expected and actual results.

    The servlet output is not an object structure, but an output stream that contains a run of strings. It is difficult to compare whether two blocks of HTML documents are "equal" or not.

Why Cactus?

The most effective way to solve the first problem is very straightforward. Since all web containers already implement these interfaces, why would we need to mock them? Cactus makes use of the web container's implementation to simplify this job. It uses an "in-container" strategy to extend the JUnit framework to the web container.

Cactus has a two-part unit test framework. One part is the traditional JUnit framework. There is an additional WebRequest, which allows user to specify request parameters and add HTTP headers. The other is the "in-container" part, which uses a special servlet to bridge the user-defined WebRequest to a real HttpServletRequest. It also provides a "join point" to interact with the test case. This join point gives test case writers a chance to add request attributes and session attributes, and call the real class (an EJB, TagLib, Servlet, or Filter class) to be tested or forward the HttpServletRequest to the real web resource (a specific servlet or JSP page).

Figure 1 shows a high-level view of Cactus' traditional unit-testing architecture.

Cactus Architecture
Figure 1. The traditional Cactus test case execution flow

Cactus also integrates HttpUnit to simplify the result comparison. HttpUnit transfers the HTML document into a WebResponse object, which contains all kinds of HTML elements, such as forms, tables, input fields, buttons, and so on. This makes the result comparison much easier.

Refer to How Cactus Works to learn more about Cactus.

Below is a simple servlet class with its test case.

// SimpleServlet.java

package unittest.cactus;



import java.io.IOException;

import java.io.PrintWriter;



import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;



public class SimpleServlet extends HttpServlet {

   //Return html document with an enabled button

   //if type param == normal

   //Otherwise, return html document with 

   //disabled button

   protected void doGet(

          HttpServletRequest request,

          HttpServletResponse response) 

          throws ServletException, IOException {

      String attr = request.getParameter("type");

      PrintWriter pw = response.getWriter();

      response.setContentType("text/html");

      pw.print(

        "<html><head/><body>");

      pw.print("<form name='form1'>");

      if (attr.equals("normal")) {

         pw.print("<input type=button 

                   name='button1' 

                   value='Click me'/>");

      } else {

          pw.print("<input type=button 

                    name='button1' 

                    value='Click me' 

                    disabled/>");

      }

      pw.print("</form>");

      pw.print("</body></html>");

   }

} 



//SimpleServletTest.java

package unittest.cactus;



import org.apache.cactus.ServletTestCase;

import org.apache.cactus.WebRequest;



import com.meterware.httpunit.Button;

import com.meterware.httpunit.HTMLElement;

import com.meterware.httpunit.WebResponse;



public class SimpleServletTest 

                        extends ServletTestCase {

    

   public SimpleServletTest(String name) {

      super(name);

   }



   protected void setUp() throws Exception {

      super.setUp();

   }



   protected void tearDown() throws Exception {

      super.tearDown();

   }



   //prepare http request parameters

   //set type parameter to normal

   public void beginDoGet1(WebRequest request) {

      request.addParameter("type", "normal");

   }

    

   //test case 1 for doGet() method 

   //in SimpleServlet

   public void testDoGet1() {

     SimpleServlet servlet = new SimpleServlet();

     try {

        servlet.doGet(request, response);

     } catch (Exception e) {

        fail("Unexpected exception: " + e);

     }

   }



   //compare the result

   public void endDoGet1(WebResponse response) {

      HTMLElement[] elements = null;

      try {

         elements = response.

                  getElementsWithName("button1");

         assertEquals(1, elements.length);

         assertFalse(((Button)elements[0]).

                                   isDisabled());

      } catch (Exception e) {

         fail("Unexpected exception: " + e);

      }

   }



   //prepare http request parameters

   //set type parameter to abnormal

   public void beginDoGet2(WebRequest request) {

      request.addParameter("type", "abnormal");

   }

    

   //test case 2 for doGet() method 

   //in SimpleServlet

   public void testDoGet2() {

      SimpleServlet servlet=new SimpleServlet();

      try {

         servlet.doGet(request, response);

      } catch (Exception e) {

         fail("Unexpected exception: " + e);

      }

   }



   //compare the result

   public void endDoGet2(WebResponse response) {

      HTMLElement[] elements = null;

      try {

         elements = response.

                  getElementsWithName("button1");

         assertEquals(1, elements.length);

         assertTrue(((Button)elements[0]).

                                   isDisabled());

      } catch (Exception e) {

         fail("Unexpected exception: " + e);

      }

   }

}

[1] [2] [3] [4] [5] Next

Close    To Top
  • Prev Article-Java:
  • Next Article-Java:
  • Now: Tutorial for Web and Software Design > Java > Java Servlets > Java Content
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Geek Tutorial
     

    Blogging Tutorial

      RSS Tutorial
      Podcasting Tutorial
    Graphic Design Tutorial
      Coreldraw Tutorial
      Illustrator Tutorial
      3D Tutorials
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial/ Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial/ Articles
     

    XML Style

      AJAX Tutorial
      XML Mobile
    Flash Tutorial/ Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial/ Articles
      Linux Tutorial
      Symbian Tutorial
      MacOS Tutorial
    Personal Tech
      Hardware Tutorial
      Software Tutorial
      Online Auction