2008/09/23

Contract Verifiers - Part IV. - The Comparison Contract Verifier

The comparison contract verifier helps in implementing correctly the generic interface IComparable. It verifies in particular that your type-specific implementation orders instances correctly.
public class Fraction : IComparable<Fration>
{
public Fraction(int numerator, int denominator)
{
// ...
}

// ...
}
As for the other contract verifiers, you must use a dedicated attribute to decorate your test fixture. And as for the equality contract verifier, your test fixture must provide classes of equivalence object instances to be consumed by the contract verifier. The only but important difference here, is that you must feed the method with ordered classes. Each class must contain instances expected to be compared strictly greater than the objects in any previous class. In other words, instances must be put in the ascending order.
[TestFixture]
[VerifyComparisonContract(typeof(Fraction))]
public class FractionTest : IEquivalenceClassProvider<Fration>
{
public EquivalenceClassCollection<Fration> GetEquivalenceClasses()
{
return EquivalenceClassCollection<Fration>.FromDistinctInstances(
new Fraction(1, 6),
new Fraction(1, 5),
new Fraction(1, 4),
new Fraction(1, 3));
};
}
}
The comparison contract verifier checks for the following features:

1. The CompareTo method works as expected. The method must return a negative value if the current object is less than the other object; a positive value if it is greater; or zero if they are equal. Furthermore, if the type is nullable, the contract verifier checks for null reference handling as well. Any non null instance should compares greater than a null reference; and two null references should be equal.
public class Fraction : IComparable<Fration>
{
public int CompareTo(Fraction other)
{
// ...
}
}
Although it is not mandatory, you might decide to override the four comparison operators. The contract verifier is able to check them too. This is an optional feature. You can disable it by setting the named parameter ImplementsOperatorOverload to false.
[TestFixture]
[VerifyComparisonContract(typeof(Fraction), ImplementsOperatorOverload = false)]
public class
FractionTest : IEquivalenceClassProvider<Fration>
{
// ...
}
2. The Greater Than (>) operator overload is correctly implemented and behaves as expected.
public class Fraction : IComparable<Fration>
{
public static bool operator >(Fraction left, Fraction right)
{
// ...
}
}
3. The Greater Than Or Equal (>=) operator overload is correctly implemented and behaves as expected.
public class Fraction : IComparable<Fration>
{
public static bool operator >=(Fraction left, Fraction right)
{
// ...
}
}
4. The Less Than (<) operator overload is correctly implemented and behaves as expected.
public class Fraction : IComparable<Fration>
{
public static bool operator <(Fraction left, Fraction right)
{
// ...
}
}
5. The Less Than Or Equal (<=) operator overload is correctly implemented and behaves as expected.
public class Fraction : IComparable<Fration>
{
public static bool operator <=(Fraction left, Fraction right)
{
// ...
}
}
It is very common to implement equality and comparison in the same time. Should this happen, you can combine several contract verifiers into the same test fixture. This is very handy.
public class Fraction : IComparable<Fration>, IEquality<Fration>
{
}
[TestFixture]
[VerifyComparisonContract(typeof(Fraction))]
[VerifyEqualityContract(typeof(Fraction))]
public class FractionTest : IEquivalenceClassProvider<Fration>
{
// ...
}

No comments: