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();
}
About these ads

Posted on November 4, 2010, in Uncategorized and tagged . Bookmark the permalink. 7 Comments.

  1. 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?

  1. Pingback: Tweets that mention Contract test for value types | Domain Driven Design using Naked Objects -- Topsy.com

  2. Pingback: Mockito-like automocking and optional autowiring in JMock « Dan Haywood

  3. Pingback: Eclipse Quick Tips – Extract Local Variable | Dan Haywood

  4. Pingback: Eclipse Tips: Quick Fix to generate code | Dan Haywood

  5. Pingback: Eclipse Tips: using the Source menu | Dan Haywood

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 248 other followers