ServletUnit Overview

As a testing tool, HttpUnit is primarily designed for "black-box" testing of web sites. In many cases that may be all you need; however, if you are developing complex servlets, you may wish to test smaller pieces of your code. Sometimes you can isolate them into simple tests using only JUnit. In other cases, you will want to test in a servlet environment. At this point you have two basic approaches available. You can test in a real servlet container, using a tool such as Apache Cactus, which has you deploy your tests into the container along with your servlets. Or you can use a simulated servlet container. ServletUnit takes the latter approach.

Getting Started with ServletUnit

To test a servlet in ServletUnit, you first instantiate a ServletRunner (the simulated container), and register your servlets:

    ServletRunner sr = new ServletRunner();
    sr.registerServlet( "myServlet", StatefulServlet.class.getName() );
Note that for complex tests, you can register multiple servlets in a single ServletRunner or initialize the ServletRunner with the name of a web.xml file from which to read an entire application full of servlets. You are now ready to begin. You need a ServletUnitClient , which performs much the same function as HttpUnit's WebConversation - in fact, they both extend the base class WebClient , so you can use it the same way, except of course that ServletUnitClient ignores the host portion of URLs in requests passed to it and goes directly to its ServletRunner . This means that you can invoke the servlet and handle its response in the same way you have been accustomed to in HttpUnit:
    ServletUnitClient sc = sr.newClient();
    WebRequest request   = new PostMethodWebRequest( "http://test.meterware.com/myServlet" );
    request.setParameter( "color", "red" );
    WebResponse response = sc.getResponse( request );
    assertNotNull( "No response received", response );
    assertEquals( "content type", "text/plain", response.getContentType() );
    assertEquals( "requested resource", "You selected red", response.getText() );

Testing Servlet Internals

The above approach is still black-box testing. To really take advantage of the power of ServletUnit, you can handle your request in steps. To do this, instead of asking the client for the final response, you ask it for an invocation context:
    ServletUnitClient sc = sr.newClient();
    WebRequest request   = new PostMethodWebRequest( "http://test.meterware.com/myServlet" );
    request.setParameter( "color", "red" );
    InvocationContext ic = sc.newInvocation( request );
This invocation context provides access to the selected servlet, which has been initialized for you with the appropriate session information, as well as the request and response objects which the servlet will process. Now you can call methods on the servlet, on the servlet session, or on the request and response objects. For example, given the following servlet definition:
    public class StatefulServlet extends HttpServlet {

        protected void doPost( HttpServletRequest req, HttpServletResponse resp ) throws ServletException,IOException {
            resp.setContentType( "text/plain" );
            writeSelectMessage( req.getParameter( "color" ), resp.getWriter() );
            setColor( req, req.getParameter( "color" ) );
        }

        void writeSelectMessage( String color, PrintWriter pw ) throws IOException {
            pw.print( "You selected " + color );
            pw.close();
        }

        void setColor( HttpServletRequest req, String color ) throws ServletException {
            req.getSession().setAttribute( "color", color );
        }
    }
you might want to test the individual methods one at a time. The following code obtains the servlet and casts it to get access to its package-level methods (the tests should be in the same package as the servlet to do this). It then invokes the setColor method to ensure that it is creating and updating the session correctly.
    StatefulServlet ss = (StatefulServlet) ic.getServlet();
    assertNull( "A session already exists", ic.getRequest().getSession( false ) );

    ss.setColor( ic.getRequest(), "blue" );
    assertNotNull( "Session was not created", ic.getRequest().getSession( false ) );
    assertEquals( "Color in session", "blue", ic.getRequest().getSession().getAttribute( "color" ) );
You can test the response from the servlet as well, if you invoke the code which creates it:
    StatefulServlet ss = (StatefulServlet) ic.getServlet();
    ic.getResponse().setContentType( "text/plain" );
    ss.writeSelectMessage( "blue", ic.getResponse().getWriter() );

    WebResponse response = ic.getServletResponse();
    assertEquals( "requested resource", "You selected blue", response.getText() );
    assertEquals( "Returned cookie count", 1, response.getNewCookieNames().length );
Note first that you must do all of the processing that the service method would have done if you take this approach. You may either call the service method itself, or a combination of other calls that will prepare the response in the fashion you wish to test. Not also that the response returned from getServletResponse is the actual one returned by the servlet, without any processing by the client. For example, if the request contains a bad status or a forward request, the client might do some additional processing, which is not done at this time. Of course, since the response extends WebResponse , all of the normal HTML parsing methods are available to examine it.

Maintaining State Across Multiple Responses

If you are using InvocationContext to access servlet internals, you are usually not completing the request cycle; as a result, the client will not process any cookies and state will not be maintained across requests. In many cases, this is OK. But if you are writing a test which depends on the maintenance of state across servlet invocations, you will want to reinvolve the ServletUnitClient , giving it a chance to process the response, including updating its own list of cookies:
    WebResponse response sc.getResponse( ic );   // pass the already processed InvocationContext

    response = sc.getResponse( "http://test.meterware.com/ReadColorFromSession" );
    assertNotNull( "No response received", response );
    assertEquals( "content type", "text/plain", response.getContentType() );
    assertEquals( "requested resource", "You posted blue", response.getText() );
    assertEquals( "Returned cookie count", 0, response.getNewCookieNames().length );
This allows any subsequent request through the same client object to take advantage of the session state established by the just completed request, just as is possible with HttpUnit.

Testing Security

If you are using a web.xml file to define your servlets, you can also test protected resources. ServletUnit supports both Basic and Form authentication, and handles them just as HttpUnit does. For Basic authentication, you must call setAuthorization passing a username and password, while Form authentication is handled just like any other form. Since ServletUnit does not maintain a database of users, it will accept any user/password combination as valid, and treat the password as a comma-separated list of role names.
Copyright © 2000-2008, Russell Gold
Hosted by SourceForge Logo