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.
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() );
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.
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.