Test First (#2)

You want to create a helper class to analyse an HttpServletRequest and provide you with helper methods which determine the type of request. You basically care about GET and POST request methods. More specifically you want to be able to tell whether a POST is Multipart (usually containing file uploads).

Without losing a second, you create the class and make it compile. Right now you only care about the API of the class, and how will it be used from the client side of view. The class is immutable.

import javax.servlet.http.HttpServletRequest;

public class HttpRequestAnalyser {

  private final boolean get;
  private final boolean post;
  private final boolean multipart;

  public HttpRequestAnalyser(HttpServletRequest request) {
    // do nothing of value (yet)
    this.get = false;
    this.post = false;
    this.multipart = false;
  }

  public boolean isGet() {
    return get;
  }

  public boolean isPost() {
    return post;
  }

  public boolean isMultipart() {
    return multipart;
  }

}

Press F9, it compiles (NetBeans).
No matter what kind of request we construct this class with, it will never give us a true answer on whether the method was GET, POST or POST-MULTIPART. That’s because the implementation is the absolute minimum we need in order to define our API and have something that compiles.

Press CTRL+SHIFT+U to create the following empty test.

import junit.framework.*;

public class HttpRequestAnalyserTest extends TestCase {
  
  public HttpRequestAnalyserTest(String testName) {
    super(testName);
  }

  public void setUp() {
  }

  public void testIsGet() {
  }

  public void testIsPost() {
  }

  public void testIsMultipart() {
  }
  
}

Press SHIFT+F6, the test passes because there are no assertions yet.

The point is to create good tests for our HttpRequestAnalyser class. The tests will fail, and then we’ll try to make them pass by doing real work in our class, which is the System Under Test.

In order to test our class we will need instances of HttpServletRequest. It’s not (easily) possible to create such instances, as this is an interface, and usually the servlet container generates concrete implementations. We don’t want to incorporate a servlet container in our tests. What we are going to do is mock an HttpServletRequest, as if it was coming over http from a client.

In order to mock such a request we could provide our own implementation, possibly in an inner static class, inside our test and we’d have to implement all methods.

Another way is to create a mock object which will implement HttpServletRequest and program it with canned answers for our class. There exist many frameworks out there for mocking and we’ll use mocquer.

We need to add the servlet-api.jar and the mocquer dependencies on our project’s test libraries.

In order to fake HTTP request data, we need to know how these look like. Firefox and many other http header monitor tools out there help us gather the following interesting parts of the requests:

GET method

GET /example.html HTTP/1.1
Host: www.domain.com

POST method

POST /example.html HTTP/1.1
Host: www.domain.com
Content-Type: application/x-www-form-urlencoded

POST method with file upload

POST /example.html HTTP/1.1
Host: www.domain.com
Content-Type: multipart/form-data; boundary=...

The test now becomes:

import javax.servlet.http.HttpServletRequest;
import junit.framework.*;
import org.jingle.mocquer.MockControl;

public class HttpRequestAnalyserTest extends TestCase {
  
  public HttpRequestAnalyserTest(String testName) {
    super(testName);
  }
  
  private MockControl requestControl;
  private HttpServletRequest request;
  
  public void setUp() {
    requestControl = MockControl.createControl(HttpServletRequest.class);
    request = (HttpServletRequest)requestControl.getMock();
  }
  
  public void testIsGet() {
    request.getMethod();
    requestControl.setReturnValue("GET");
    requestControl.replay();
    HttpRequestAnalyser analyser = new HttpRequestAnalyser(request);

    assertTrue(analyser.isGet());
    assertFalse(analyser.isPost());
    assertFalse(analyser.isMultipart());
  }
  
  public void testIsPost() {
    request.getMethod();
    requestControl.setReturnValue("POST");
    request.getHeader("Content-Type");
    requestControl.setReturnValue("application/x-www-form-urlencoded");
    requestControl.replay();
    HttpRequestAnalyser analyser = new HttpRequestAnalyser(request);

    assertFalse(analyser.isGet());
    assertTrue(analyser.isPost());
    assertFalse(analyser.isMultipart());
  }
  
  public void testIsMultipart() {
    request.getMethod();
    requestControl.setReturnValue("POST");
    request.getHeader("Content-Type");
    requestControl.setReturnValue("multipart/form-data; boundary=...");
    requestControl.replay();
    HttpRequestAnalyser analyser = new HttpRequestAnalyser(request);

    assertFalse(analyser.isGet());
    assertTrue(analyser.isPost());
    assertTrue(analyser.isMultipart());
  }
  
}

We’ve provided method call expectations, and canned answers on the mock. The tests fail.
We fill in the HttpRequestAnalyser constructor implementation which now becomes:

import javax.servlet.http.HttpServletRequest;

public class HttpRequestAnalyser {
  
  private final boolean get;
  private final boolean post;
  private final boolean multipart;
  
  public HttpRequestAnalyser(HttpServletRequest request) {
    this.get = request.getMethod().equals("GET");
    this.post = request.getMethod().equals("POST");
    this.multipart =
        request.getHeader("Content-Type").startsWith("multipart/");
  }
  
  public boolean isGet() {
    return get;
  }
  
  public boolean isPost() {
    return post;
  }
  
  public boolean isMultipart() {
    return multipart;
  }
  
}

Post and multipart tests pass. The get test fails, because we haven’t set an expectation for getHeader(“Content-Type”) to be called. That is because the GET Http method doesn’t have such a header.
The multipart check should only happen if the request has been recognised to be a post:

import javax.servlet.http.HttpServletRequest;

public class HttpRequestAnalyser {
  
  private final boolean get;
  private final boolean post;
  private final boolean multipart;
  
  public HttpRequestAnalyser(HttpServletRequest request) {
    this.get = request.getMethod().equals("GET");
    this.post = request.getMethod().equals("POST");
    this.multipart = post ?
      request.getHeader("Content-Type").startsWith("multipart/") :
      false;
  }
  
  public boolean isGet() {
    return get;
  }
  
  public boolean isPost() {
    return post;
  }
  
  public boolean isMultipart() {
    return multipart;
  }
  
}

Tests pass, and the class is ready to rock! Happy testing (before coding).

Comments are closed.