Contract test for value types
In my 2-day TDD course a couple of the things we go into are value types and of contract tests. It occurred to me recently (while writing a new value type within Apache Isis) that I really should have a contract test for any value type. That would allow me to easily check that the value type’s equals() method was an equivalence relation (ie symmetric, transitive, reflexive) and that its hashCode() is consistent with equals().
What I came up with is the ValueTypeContractTestAbstract<T> class. This is how you would use it to test that java.lang.String#equals() meets the equivalence relation:
public class ValueTypeContractTestAbstract_StringTest
extends ValueTypeContractTestAbstract<String> {
@Override
protected List<String> getObjectsWithSameValue() {
return Arrays.asList(new String("1"), new String("1"));
}
@Override
protected List<String> getObjectsWithDifferentValue() {
return Arrays.asList(new String("1 "), new String(" 1"), new String("2"));
}
}
Similarly, the tests for java.math.BigDecimals looks like:
public class ValueTypeContractTestAbstract_BigIntegerTest
extends ValueTypeContractTestAbstract<BigInteger> {
@Override
protected List<BigInteger> getObjectsWithSameValue() {
return Arrays.asList(new BigInteger("1"), new BigInteger("1"));
}
@Override
protected List<BigInteger> getObjectsWithDifferentValue() {
return Arrays.asList(new BigInteger("2"));
}
}
And what of the contract test itself? Here, for you to copy-n-paste is the class:
package org.apache.isis.testsupport;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.*;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
/**
* Contract test for value types ({@link #equals(Object)} and {@link #hashCode()}).
*/
public abstract class ValueTypeContractTestAbstract<T> {
@Before
public void setUp() throws Exception {
assertSizeAtLeast(getObjectsWithSameValue(), 2);
assertSizeAtLeast(getObjectsWithDifferentValue(), 1);
}
private void assertSizeAtLeast(List objects, int i) {
assertThat(objects, is(notNullValue()));
assertThat(objects.size(), is(greaterThan(i-1)));
}
@Test
public void notEqualToNull() throws Exception {
for(T o1: getObjectsWithSameValue()) {
assertThat(o1.equals(null), is(false));
}
for(T o1: getObjectsWithDifferentValue()) {
assertThat(o1.equals(null), is(false));
}
}
@Test
public void reflexiveAndSymmetric() throws Exception {
for(T o1: getObjectsWithSameValue()) {
for(T o2: getObjectsWithSameValue()) {
assertThat(o1.equals(o2), is(true));
assertThat(o2.equals(o1), is(true));
assertThat(o1.hashCode(), is(equalTo(o2.hashCode())));
}
}
}
@Test
public void notEqual() throws Exception {
for(T o1: getObjectsWithSameValue()) {
for(T o2: getObjectsWithDifferentValue()) {
assertThat(o1.equals(o2), is(false));
assertThat(o2.equals(o1), is(false));
}
}
}
@Test
public void transitiveWhenEqual() throws Exception {
for(T o1: getObjectsWithSameValue()) {
for(T o2: getObjectsWithSameValue()) {
for(Object o3: getObjectsWithSameValue()) {
assertThat(o1.equals(o2), is(true));
assertThat(o2.equals(o3), is(true));
assertThat(o1.equals(o3), is(true));
}
}
}
}
protected abstract List<T> getObjectsWithSameValue();
protected abstract List<T> getObjectsWithDifferentValue();
}
Posted on November 4, 2010, in Uncategorized and tagged tdd. Bookmark the permalink. 7 Comments.
I like the elimination of duplication in test code of this approach. Is “Contract test” a particular term, such as a pattern, or only a generic name?
I guess it’s the name of a design pattern for testing. I came across it when I was working on my TDD course a while back; a bit of googling will throw up the occasional use (eg in the comments of http://www.markhneedham.com/blog/2009/09/18/tdd-testing-with-generic-abstract-classes/, and in http://my.opera.com/karmazilla/blog/2009/09/10/four-kinds-of-tests-for-basic-correctness).
Actually, I think it was something from JB Rainsberger that I originally read that introduced me to the term.
Pingback: Tweets that mention Contract test for value types | Domain Driven Design using Naked Objects -- Topsy.com
Pingback: Mockito-like automocking and optional autowiring in JMock « Dan Haywood
Pingback: Eclipse Quick Tips – Extract Local Variable | Dan Haywood
Pingback: Eclipse Tips: Quick Fix to generate code | Dan Haywood
Pingback: Eclipse Tips: using the Source menu | Dan Haywood