Real Fakes…

I’ve been reading a lot about mocks lately. A couple of good articles from Uncle Bob, in particular When to Mock and The Little Mocker. I’m not very good at employing mocks so these articles have been enlightening. There is a lot I want to apply and grow my skills.

One thing I saw in these articles, and hear consistently from my peers is that without employing these strategies testing against web servers is too slow to be viable. I don’t feel that’s an absolute truth. If the situation is right it can be done. Here’s how it can work.

I worked a project were the servers were pretty light weight java servlets that dispatched JSON RPC requests. We started with various levels of mocking but then we tried a different technique. Using embedded Jetty we fired up the servlets right in process. Real fakes. The servlets were light weight, and started quickly, and the tests hit the end points in process. They ran fast and saved us a lot of effort trying to mock implementations, and make sure those implementations stayed up to date. When the servlet changed, you immediately knew how that effected the contracts because the tests were testing the latest and greatest.

Java Contract Testing, a Pragmatic Approach With JUnit4

I recently read the thought provoking article, Simplifying Contract Testing. It does a good job of discussing how Java interfaces act as contracts, and highlighting that method signatures go only so far towards defining the contract, pointing out that the comments/documentation end up describing key contractual behaviors. With key aspects of the contract appearing only in the text, writing useful contract tests is a valuable goal. The article also offered a JUnit solution, but that’s where it fell apart for me. The solution was thorough, but too complex for my tastes, with custom annotations, gratuitous dependency injection, and as many as three extra support classes to test an interface/implementation pair.

Inspired by the topic, I decided to return to the stated problem and look for a simpler, more pragmatic solution. In the end, I came up with a decent approach, using just stock JUnit4. So, lets start at the beginning.

Defining “The Contract”

So what is an interface’s contract? Lets use Iterator as an example. Stripped down, the code defines the method signature contract as:

public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}

That much is enforced by Java when you implement it. But that isn’t the full contract. Looking at the documentation there are other specified behaviors making up the contract, for example:

  • next should throw NoSuchElementException if hasNext would return false.

Enforcing the Contract With Tests

Since the contract is more then the interface signatures, and can’t effectively be enforced by the compiler, you’ll want to write tests to enforce it, right? These tests will cover the behavior of the signatures that are only described in the documentation. The question becomes, how do you do that efficiently? If your project has a bunch of classes that implement some interface you obviously want to avoid adding code to test the interface’s contract to each implementation’s tests.

A Pragmatic Approach With JUnit4

If you are using JUnit4, here is an outline of a pragmatic approach to contract testing:

  1. Make sure JUnit is only running *Test.java files. With maven’s surefire you do this via <include>**/*Test.java</include>
  2. Create an abstract *Contract.java for contracts you want to test, give them abstract methods to access resources needed by the tests.
  3. Add @Test methods to the *Contract.java to test the desired behaviors.
  4. Have the *Test.java implementation test classes extend the abstracts appropriately.

The Iterator Example

As an example we’ll create the contract test for the Iterator behavior we noted. Create IteratorContact.java:

public abstract class IteratorContract<T> {

    protected abstract Iterator<T> getNonEmptyIterator();

    @Test(expected=NoSuchElementException.class)
    public void nextThrowsExceptionWhenIteratorIsAtEnd() 
                           throws Exception {
        Iterator<T> anIterator = getNonEmptyIterator();
        assert(anIterator != null);
        assert(anIterator.hasNext() != null);
        while(anIterator.hasNext()) {
            anIterator.next();
        }
        anIterator.next();
    }
}

Since the abstract class is not a *Test.java file, JUnit will not try to run its tests. Now create a contract test for any concrete implementations. Take for example an EnumerationIterator which creates an Iterator adapter for an Enumeration. The implementation test would be EnumerationIteratorTest.java and look like:

public class EnumerationIteratorTest 
            extends IteratorContract<String> {
    @Override
    protected Iterator<String> getNonEmptyIterator() {
        Vector<String> strings = 
                 new Vector<String>();
        strings.add("a");
        return new EnumerationIterator(
                     strings.elements());
    }
    // Any other tests you want ...
}

JUnit4 will run that test because its a *Test.java file. The getNonEmptyIterator() will provide an instance to test, and the inherited @Test nextThrowsExceptionWhenIteratorIsAtEnd() method will test the behavior of the contract behavior we desired. For any other Iterator you’ve implemented, just add another *Test.java extending the IteratorContract appropriately. Rinse, lather, repeat.

In Summary

For a single Iterator this isn’t a particularly compelling approach but consider an interface like Comparable, it has only one method, but that one method has at least five key contractual behaviors found only in the documentation:

  • equals returns 0
  • less than returns < 0
  • greater than returns > 0
  • throw runtime exception NullPointerException if the argument is null
  • throw runtime exception ClassCastException if the argument can’t be cast to the current class

If your project has implementations of Comparable sprinkled throughout, which isn’t unrealistic, a contract test implemented as described can get you completely consistent contract testing coverage with minimal code.