Archive for the ‘xp’ Category

Test First (#2)

Wednesday, August 1st, 2007

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

Unit testing JavaScript with JsUnit

Sunday, June 17th, 2007

my-script.js

function capitalize(str) {
  return str?
    str.length>1?str[0].toUpperCase()+str.substring(1):
      str.toUpperCase():str;
}

This is my JavaScript function for capitalizing the first letter of a String. I was sure that I got capitalization right, but I wasn’t too sure about how this function would handle null/empty values.

So I wrote a unit test, for JsUnit:

my-script.test.html

<script language="javascript" src="jsUnitCore.js"></script>
<script language="javascript" src="my-script.js"></script>
<script language="javascript">

function testCapitalize() {
  assertEquals("Foo bar", capitalize("foo bar"));
  assertEquals("Foobar", capitalize("foobar"));
  assertEquals("F", capitalize("f"));
  assertEquals("", capitalize(""));
  assertEquals(JSUNIT_UNDEFINED_VALUE, capitalize(JSUNIT_UNDEFINED_VALUE));
}

</script>

The test requires loading the jsUnitCore.js script. This test loads a local copy but you could use the online version as well. Then I can run the test with my local TestRunner.

Status: Done (0.156 seconds)
Runs: 1   Errors: 0   Failures: 0

Now I know that my function is correct!

When a test fails the assertion error looks like this:

1. my-script.test.html:testCapitalize failed

Expected <Foobar> (String) but was <foobar> (String)

Stack trace follows:
> JsUnitException
> _assert
> assertEquals
> testCapitalize

Happy testing!

Test First (#1)

Saturday, April 28th, 2007

Unit testing not only ensures that the code you write is correct, but also helps you develop your code. This can be achieved by testing unimplemented methods and functionalities first, and then “filling in” the code to satisfy the tests. This is the “test first” rule that your XP coworker will always remind you, at any occasion. (You do have an XP coworker, don’t you? :)

The Requirement

Suppose you have a webapp which displays some data from a database. Some of those texts are long, and you are asked to be able to truncate them at predefined lengths on some particular views. So instead of printing “Lorem ipsum dolor sit” you should be able to fit that in 12 chars and print “Lorem ips…”.

Wait

The first worst thing you could do there would be to stick that logic straight into the view (JSP, velocity or whatever templating engine you use). You will not be able to test that piece of logic, nor easily reuse it in some other project. You will need to do this in a Java class and then find a way to call it from the template.

The second worst thing you could do would be to implement this functionality yourself, as it already exists in commons lang. You should know what APIs exist out there and try to reuse as often as possible. But anyhow, we’ll assume that you want to do it yourself.

Think

You start by thinking of where to place this method and whether it will be a helper (static?) method or part of a full fledged class with state etc. Then you choose a good method name and what parameters it will accept. Think of how you would like to use this method.

Code

You write the method signature:

public static String truncate(String text, int length) {
}

Then fill it’s body to the absolute minimum to make it “compilable”:

public static String truncate(String text, int length) {
  throw new RuntimeException("not implemented");
}

…or…

public static String truncate(String text, int length) {
  return "";
}

And then stop, because that’s all you need to implement for now.

Test

You create a test (hit CTRL+SHIFT+U if you use http://www.netbeans.org/) and create a test for the method truncate

public void testTruncate() {
}

This test passes, because it doesn’t test anything. You make things more interesting by adding some basic assertions of what you expect from this method.

public void testTruncate() {
  assertEquals("Lorem ip...", Demo.truncate("Lorem ipsum dolor sit", 11));
  assertEquals("Lorem ips...", Demo.truncate("Lorem ipsum dolor sit", 12));
  assertEquals("Lorem ipsu...", Demo.truncate("Lorem ipsum dolor sit", 13));
}

You run the test and it fails.
You go back to the source code and implement some logic to satisfy this test.

Code

public static final String truncate(String text, int length) {
  return text.substring(0, length-3) + "...";
}

You run the test and it now passes.

Test

It’s time to test for some corner cases.

public void testTruncate() {
  assertEquals("", Demo.truncate("Lorem", 0));
  assertEquals(".", Demo.truncate("Lorem", 1));
  assertEquals("..", Demo.truncate("Lorem", 2));
  assertEquals("...", Demo.truncate("Lorem", 3));
  assertEquals("Lorem ip...", Demo.truncate("Lorem ipsum dolor sit", 11));
  assertEquals("Lorem ips...", Demo.truncate("Lorem ipsum dolor sit", 12));
  assertEquals("Lorem ipsu...", Demo.truncate("Lorem ipsum dolor sit", 13));
  assertEquals("Lorem ipsum dolor sit", Demo.truncate("Lorem ipsum dolor sit", 21));
}

All new assertions fail with a StringIndexOutOfBoundsException. You write code to satisfy these corner cases.

Code

public static final String truncate(String text, int length) {
  switch(length) {
    case 0: return "";
    case 1: return ".";
    case 2: return "..";
    case 3: return "...";
    default:
      return length<text.length() ?
        text.substring(0, length-3) + "..." :
        text;
  }
}

You run the test and it now passes.

Test

Then you test even more.

public void testTruncate() {
  assertEquals("", Demo.truncate("Lorem", 0));
  assertEquals(".", Demo.truncate("Lorem", 1));
  assertEquals("..", Demo.truncate("Lorem", 2));
  assertEquals("...", Demo.truncate("Lorem", 3));
  assertEquals("Lorem ip...", Demo.truncate("Lorem ipsum dolor sit", 11));
  assertEquals("Lorem ips...", Demo.truncate("Lorem ipsum dolor sit", 12));
  assertEquals("Lorem ipsu...", Demo.truncate("Lorem ipsum dolor sit", 13));
  assertEquals("Lorem ipsum dolor sit", Demo.truncate("Lorem ipsum dolor sit", 21));
  try {
    Demo.truncate("Lorem ipsum dolor sit", -1);
    fail("Should have thrown illegal argument exception");
  } catch (IllegalArgumentException expected) { }
}

This is a standard idiom for testing that an exception should be thrown. If this method is called with a negative length parameter, we’d like an IllegalArgumentException to be thrown. It is not mandatory to test for things like these, but some people like to seal their methods from really bad usage.

Code

public static final String truncate(String text, int length) {
  if (length<0) {
    throw new IllegalArgumentException("Length cannot be negative");
  }
  switch(length) {
    case 0: return "";
    case 1: return ".";
    case 2: return "..";
    case 3: return "...";
    default:
      return length<text.length() ?
        text.substring(0, length-3) + "..." :
        text;
  }
}

You run the test and it now passes. You are done.

Conclusion

This implementation might not be the best in the world, but right now this doesn’t matter. The code runs, and it’s robust. Whenever you feel like, you can refactor it and make it perform better. The test will be there to guide you.

p.s What we’ve omitted when we wrote the signature of this method, was to write Javadoc. It is very important to document your API, and we’ll discuss that in another post.

Unit testing needs time?

Tuesday, March 20th, 2007

New developers will sometimes complain about how Unit Testing requires a lot of time. How much it slows them down, and how they cannot see any good in writing tests for their code.
There are many scenarios which prove that unit testing is necessary. These include speed of development, ability to refactor easily, test-driven development, testing without the need of web container, testing with mock objects etc.

My favorite though is the “client calls to report something weird” scenario. I’ve seen it many times and it goes something like this:

Scenario:

  1. Your webapp is deployed, weeks ago, and you’ve moved on with a new, exciting project. Everything is feels good, as you’ve completely forgotten about sins of the past (not writing tests)
  2. Client calls you to report “something weird”.
  3. You stop whatever you are doing at that moment to switch to that project.
  4. You connect (VPN or whatever) to the remote server to see possibly logged exceptions.
  5. You collect your data.
  6. You try to reproduce the error locally, on the development server.
  7. You find the bug.
  8. You issue the bug in the bug tracking system.
  9. You fix the bug.
  10. You build for deployment.
  11. You deploy (while solving any possible application version issues).
  12. You contact the client.
  13. Wait for his confirmation that the bug has been corrected.
  14. You deliver the bug in the issue tracking system.
  15. Finally you commit the code back to SVN.

…or in whatever order feels more natural to you.

If the above scenario feels OK, and you need some hints on why you should try to minimize such cases, have a look at the costs involved:

Costs:

  • All of these actions need time. Your time.
  • Most of them require a context switch. Not only you lose X minutes from your previous work, but also need Y minutes to get back into the flow (mind state) you had previously.
  • Some of these steps might not be what you really want to be doing (talking directly to the client).
  • You become a slow worker producing bad code.
  • People will never trust you with that mission critical application, because your code has a tendency to develop “random features” on runtime (usually involving exciting names such as NullPointerException).

Facts:

This scenario can definitely happen for tested code. Bugs will always creep into your code no matter what. The point is try to at least minimize the stupid ones. Cases which can easily be covered by unit tests.
Unit testing is important (if not mandatory). If you feel that it needs time, you have to press yourself and do it. It’s a matter of weeks until you become test infected and experience how your software becomes better in less time.

Hints for NetBeans users:

  • Got a class that you want to create a JUnit test for? CTRL+SHIFT+U
  • Got a class and want to jump to the unit test (and vice versa)? ALT+SHIFT+E
  • Want to test the project? ALT+F6

Happy testing.