Friday, July 3, 2009

Apache Commons Lang: CompareToBuilder() Example with some TDD

In a previous post, I talked about using commons-lang to implement equalsTo(), hashCode(), and toString(), using the Builders provided by the library. Now I've had some time to play with CompareToBuilder and I thought I would take a small step at presenting an example using with some TDD thrown in after reading Kent Beck's Test Driven Development. As this is my first attempt at TDD, please forgive any sins against the methodology :)

My object under test has two simple properties:

import org.apache.commons.lang.builder.EqualsBuilder;

public class Namespace {
private Integer id;
private String uri;
...
@Override
public boolean equals(Object object) {
return EqualsBuilder.reflectionEquals(this, object);
}
}

Before I start to implement comparesTo(), I write my first test under the assumption that two equals Namespaces should compare equally:
void testEqualsAndCompareTo() {
def namespace = new Namespace(id, uri)
def equalNamespace = new Namespace(id, uri)
assertEquals namespace, equalNamespace
assertEquals 0, namespace.compareTo(equalNamespace)
}

The test of course fails before it runs as there is no Namespace.compareTo() method. To get to a green bar quickly, I add the method, not providing the final implementation, to get the green bar:
public int compareTo(Object o) {
return 0;
}

The test passes, so it's time for another test, comparing two Namespaces with unequal id properties:
void testCompareToWithGreaterId() {
def namespace = new Namespace(id, uri)
def greaterNamespace = new Namespace(id + 1, uri)
assertEquals(-1, namespace.compareTo(greaterNamespace))
}

It is worth noting omitting the parentheses in the assertEquals call causes a Groovy compiler error. Red bar:
junit.framework.AssertionFailedError: expected:<1> but was:<0>

Now the debate starts about how far I should go to get a green bar. I could implement the method incrementally to achieve the happy green state, or go with my first shot at the final implementation. I know I want to use CompareToBuilder so I'll start there. Hopefully, this falls somewhere in the middle:

import org.apache.commons.lang.CompareToBuilder;
//...
public class Namespace() {
//...
public int compareTo(Object o) {
Namespace namespace = (Namespace) o;
return new CompareToBuilder()
.append(this.getId(), namespace.getId())
.toComparison();
}
}

Back to green. Another test for the case when the id is lesser:

void testCompareToWithLesserId() {
def namespace = new Namespace(id, uri)
def lesserNamespace = new Namespace(id - 1, uri)
assertEquals 1, namespace.compareTo(lesserNamespace)
}

Whew... still green... let's write a test that includes the uri property:
void testCompareToWithGreaterUri() {
def uri = "a"
def greaterUri = "z"
def namespace = new Namespace(id, uri)
def greaterNamespace = new Namespace(id, greaterUri)
assertEquals(-1, namespace.compareTo(greaterNamespace)
}
Sweet crimson:
testCompareToWithGreaterUri(org.oclc.harbor.NamespaceTest)
Time elapsed: 0.004 sec <<< FAILURE!
junit.framework.AssertionFailedError: expected:<-1> but was:<0>

Back to the method under test:
public int compareTo(Object o) {
Namespace namespace = (Namespace) o;
return new CompareToBuilder()
.append(this.getId(), namespace.getId())
.append(this.getUri(), namespace.getUri())
.toComparison();
}
Green. Another test like before:
void testCompareToWithLesserUri() {
def uri = "z"
def lesserUri = "a"
def namespace = new Namespace(id, uri)
def lesserNamespace = new Namespace(id, lesserUri)
assertEquals 1, namespace.compareTo(lesserNamespace)
}

Red? Maybe I don't understand comparison like I probably should.
junit.framework.AssertionFailedError: expected:<1> but was:<25>
Eh, it's getting late, but I think I need to be testing that the result is simply greater than zero? I'm thinking the result was 25 as 'z' is 25 characters greater than 'a'. The Comparable API states that I should be looking for a a negative integer, zero, or a positive integer:
assertTrue namespace.compareTo(greaterNamespace) > 0
Green, but I should probably due more research...

An alternative to the compareTo() method above is to use reflection to implement the method:

public int compareTo(Object o) {
return CompareToBuilder.reflectionCompare(this, o);
}
Do the tests still pass? Yeah, is this a step towards the "final" implementation? Some might argue this approach is better because the first implementation requires maintenance if a property was added or removed. Others might not trust the reflective nature of the second method. Thoughts?

No comments:

Post a Comment