How do I Unit Test a servlet?

2019-05-07 06:50发布

问题:

I have a servlet called Calculator. It reads the parameters left, right and op and returns by setting an attribute result in the response.

What is the easiest way to unit test this: basically I want to create an HttpServletRequest, set the parameters, and then checking the response - but how do I do that?

Here's the servlet code (it's small and silly on purpose):

public class Calculator extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
public Calculator() {
    super();
}       
protected void doGet(HttpServletRequest request, HttpServletResponse response) 
    throws ServletException, IOException {
}   
protected void doPost(HttpServletRequest request, HttpServletResponse response) 
    throws ServletException, IOException {
    Integer left = Integer.valueOf(request.getParameter("left"));
    Integer right = Integer.valueOf(request.getParameter("right"));
    Integer result = 0;
    String op = request.getParameter("operator");
    if ("add".equals(op)) result = this.opAdd(left, right);
    if ("subtract".equals(op)) result = this.opSub(left, right);
    if ("multiply".equals(op)) result = this.opMul(left, right);
    if ("power".equals(op)) result = this.opPow(left, right);
    if ("divide".equals(op)) result = this.opDiv(left, right);
    if ("modulo".equals(op)) result = this.opMod(left, right);

    request.setAttribute("result", result); // It'll be available as ${sum}.
    request.getRequestDispatcher("index.jsp").forward(request, response);
    }
}
...

}

回答1:

Often, the important logic of a program is factored out into other classes, that are usable in a variety of contexts, instead of being tightly coupled to a Servlet Engine. This leaves the servlet itself as a simple adapter between the web and your application.

This makes the program easier to test, and easier to reuse in other contexts like a desktop or mobile app.



回答2:

There are a few libraries out there that you can use. Are you using Spring http://www.springsource.org/ in your application? If so, there is one application for spring (spring-test) that contains MockHttpServletRequest. For example:

@Test
public void shouldReturnAValidaRedirectionMessage() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    request.addParameter("op", "addition");
    request.addParameter("left", "1");
    request.addParameter("right", "5");

    CalculatorServlet servlet = new CalculatorServlet();
    Operation operation = servlet.getOperation(request);
    assertNotNull(operation);
    assertEquals(ADDITION, operation.getOperationType());
            ...


回答3:

Check out ServletUnit. It's part of HttpUnit.

http://httpunit.sourceforge.net/doc/servletunit-intro.html



回答4:

Can't say this is the best method to do so : but to unit test a simple servlet like that (one not using forwards, context etc..) what you could simply do is :

  1. Create mock HttpServletReqeust and HttpServletResponse instances using any mocking library. Even simpler would be using RequestWrapper and ResponseWrapper classes (simple custom classes implemented by extending the HttpServletReqeust and HttpServletResponse classes).
  2. On these mock (or custom) instances set certain properties - the parameters you want to test against in each test case - e.g. op=add for a addition unit test. If you are using custom classes, you can simply set them in an internal properties object. If you are using mocks, then settings expectations would do.
  3. Create an instance of the servlet - new Calculator(), keeping the required libs in the class path. Now call the service method on this instance.
  4. When the call returns, get the o/p from the response class and assert it. Since the response class is again a custom class or a mocked version, this should be easy.

For mocking, a simply starting point would be EasyMock or Mockito (my fav)

An example for the wrapper : http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpServletRequestWrapper.html

HTH



回答5:

Generally you should abstract your business logic from Servlet container details. You can mock ServletRequest using Spring test package, but it would be a bad idea to simulate Servlet container. So, you should either run system tests on a real container or move your logic from servlet into a separate bean and test it in isolation.

  public class Calculator {
    public Integer calculate(Integer left, Integer right, String op) {
      Integer result = 0;
      if ("add".equals(op)) result = this.opAdd(left, right);
      if ("subtract".equals(op)) result = this.opSub(left, right);
      if ("multiply".equals(op)) result = this.opMul(left, right);
      if ("power".equals(op)) result = this.opPow(left, right);
      if ("divide".equals(op)) result = this.opDiv(left, right);
      if ("modulo".equals(op)) result = this.opMod(left, right);
      return result;
    }
  }


  public class CalculatorServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
     Integer left = Integer.valueOf(request.getParameter("left"));
     Integer right = Integer.valueOf(request.getParameter("right"));
     String op = request.getParameter("operator");
     Integer result = calculator.calculate(left, right, op);
     request.setAttribute("result", result); // It'll be available as ${sum}.
     request.getRequestDispatcher("index.jsp").forward(request, response);
   }
 }