Tuesday, March 27, 2012

Pleasant surprise: JUnit supports Design by Contract

I've long felt contract envy of languages like Eiffel supporting Design by Contract. Only recently, I stumbled over the (not so) recent addition of theories to JUnit. Once the object instances under test are separated from the test-code - now taking on the form of a logical implication, making the latter re-usable is the obvious next step. Ever been tired of writing tests for your value-object's equals and hashCode methods? Try this:
/**
 * Provides JUnit theories that check, whether an arbitrary class 
 * satisfies the Object interface contract regarding the methods 
 * equals and hashCode. Instances of the class(es) to be tested 
 * have to be provided in derived classes 
 * 
 * @author scm
 */
@RunWith(Theories.class)
public abstract class ObjectContractTheory {
    
    @DataPoint
    public static final Object INSTANCE_OF_UNRELATED_TYPE = new String();
    
    @Theory
    public void equality_is_symmetrical( Object o1, Object o2) {
        assumeThat( o1, equalTo( o2 ) );

        assertThat( o2, equalTo( o1 ) );
    }

    @Theory
    public void equality_implies_equal_hashCodes( Object o1, Object o2) {
        assumeThat( o2, notNullValue() );
        assumeThat( o1, equalTo( o2 ) );

        assertThat( o1.hashCode(), is( equalTo( o2.hashCode() ) ) );
    }
        
    @Theory
    public void identity_implies_equality( Object o1, Object o2 ) {
        assumeThat( o1, sameInstance( o2 ) );

        assertThat( o1, equalTo( o2 ) );
    }

    @Theory
    public void any_object_is_unequal_to_null( Object o1 ) {
        assumeThat( o1, notNullValue() );
        
        assertThat( o1, not(  equalTo( null ) ) );
    }

    @Theory
    public void unrelated_classes_are_not_equal( Object o1, Object o2 ) {
        assumeThat( o1, notNullValue() );
        assumeThat( o2, notNullValue() );
        assumeThat( o1,  not( instanceOf( o2.getClass() ) ) );
        assumeThat( o2,  not( instanceOf( o1.getClass() ) ) );
        
        assertThat( o1, not( equalTo( o2 ) ) );
    }


}


Then to make sure that Integer fulfills that part of the object contract, you would simply write:
public class ObjectContractTest extends ComparableContractTheory {    
    @DataPoints
    public static final Integer[] INTS = {1,2,3,4};
}
Which is hardly to much to ask, even if creating your really interesting objects were slightly more verbose.