Separating the Servlet API from the Servlet Logic

The above tests for the doPost method required some relatively expensive preparation, because we had to create a mock user database and pack it into the servlet context, and we set specific parameters in the request object. In addition, our validation steps made a detour: from servlet (to DummyServletContext, further to DummyRequestDispatcher, from DummyHttp-ServletRequest to DummyHttpSession).

The larger the number of parameters and helper objects required, the more difficult becomes the development and adaptation of our test cases. If we find that the test case configuration and/or validation makes us lose track of things, then this is a sign that the design includes too many dependencies. In the case discussed here, the structure of the servlet API is the first thing to blame, because it couples the servlet itself to request, response, configuration, context, and—over detours—to the dispatcher. However, we should not use this as an excuse to passively tolerate the design flaws of this API. One way to minimize the dependence of our servlet logic upon the servlet API and thus make the servlet more testable is to move all API functions that the servlet needs to do its internal logic to a separate interface. For our sample login servlet, such an interface could look like this:

import javax.servlet.http.*;
public interface ServletInvocation {
 String getName();
 String getPassword();
 UserDatabase getUserDatabase();
 HttpSession createSession();
 void forwardTo(String uri);

The name ServletInvocation suggests that a new instance is created for each servlet invocation This approach avoids the concurrency problems inherent in servlet objects. [8] The interface in the form shown here is specialized to the LoginServlet. But experience has shown that a large number of methods needed in all servlets or at least in one group of servlets evolves quickly. To be able to replace the ServletInvocation interface by its mock counterpart in our tests, we need a corresponding "factory":

import javax.servlet.*;
import javax.servlet.http.*;
public interface ServletInvocationFactory {
 ServletInvocation createInvocation(
 HttpServletRequest request,
 HttpServletResponse response,
 ServletContext context);

The interface and class mesh under development, including the pertaining implementations, now looks as shown in Screenshot. If we now put the factory into ServletContext, then each doPost() invocation initially creates the invocation object and then proceeds with the normal servlet logic. The test cases for the servlet itself are now much clearer thanks to the use of the mock factory and mock invocation. We need a few additional tests for the real ServletInvocation implementation. The practical realization of this concept is an ideal exercise for all readers.

Java Click To expand
Screenshot: ServletInvocation.

Moving functions of the servlet API into a separate object is only one variant of the idea, How do I make the controller logic of the servlet independent of the servlet API, so that it is better testable? This principle has also been generically implemented in servlet frameworks, such as Apache's Turbine [URL:Turbine] and Struts (discussed later in this chapter). [8]One of the most common errors in servlet coding is the use of variable instance variables within the servlet class.