Before anyone gets confused I am using a very broad definition of contract that it is not related at all with Code Contracts or Design by Contract.
What I mean by contract is the correct implementation of an interface that has become idiomatic. And before you ask, an interface is not a contract. I consider it part of the definition of a contract, but it lacks on the definition of the obligations of client and supplier and most importantly, the verification of such contract. And also before you ask, no, Code Contracts do not provide all the means to express complex obligations and outcomes when conditions are met.
One of such contracts that are pervasive in the .NET Framework is
IComparable<T>. This useful interface define how two types can be compared for the purpose of sorting. As a matter of fact, it is so useful I have already written about it in the past in the Equality and Comparison series. One missing point in such series is how to check that we have fulfilled all the un-checked obligations (they are written, although scattered, in the interface and operators documentation).
Other testing frameworks, such as MbUnit, have had a way to verify those obligations for some time now, in the shape of contract verifiers. Their implementation of the idea is such that complete test fixtures are created for the verification purpose, making my endeavor of porting such functionality to the NUnit I use and almost love a pretty difficult one, as that would make me dig deeper than I feel compelled to in the inner working of its provider model. Besides, MbUnit’s syntax is not something I am very fond of, so a re-write was bound to happen.
Framing the Problem
The problem with comparison if not very complex, but it has to be done right.
In its simplest form, can be defined that types should return a negative value when compared against a bigger value, a positive value when compared against a smaller value and zero when compared against an equal value. Oh, and one has to keep in mind those pesky nulls. Oh, and one has a handful operators that could ne checked too. Oh, and one can compare types against other types, not just itself. Oh, and what we have to check is the same regardless of the type that implements the comparison we are testing.
It is easy to see that the number of combinations and the fact they are repetitive make the subject a perfect candidate to receive a helping hand.
The helping hand
Testing? Asserting for correctness? Looks like a task for Testing.Commons.NUnit. And indeed it is.
To start with, the problem can be presented like an assertion over several values and can be split into several parts: the implementation of
T the same type of the instance being asserted; the implementation of
T is not the same type as the instance being asserted; the implementation of comparison operators (
<, >, >=, <=) against the same type as the instance being asserted; and the implementation of comparison operators against other types.
For the first two cases (the implementation of
IComparable<T>), the custom constraint
ImplementsComparableConstraint is used, maybe through the
Must.Satisfy.ComparableSpecificationAgainst() extension. Its usage is really simple:
Provide either two or three instances of the type it is being compared against, one that is greater and one that is smaller and maybe one that is equal (when comparison to itself is not possible) and the combinations with null and not null values will be checked, providing a nice informative message when the type does not honor the contract.
For the next two cases (the implementation of comparison operators) the custom constraint
ImplementsComparisonConstraint is used, maybe using the
Must.Satisfy.ComparisonSpecificationAgainst(). Again, the usage is really simple:
Again, just provide some instances to compare against and the right combinations of null and not null values would be checked. Error messages are likewise descriptive:
The implementation itself is not terribly sophisticated once the problem is laid out. But there are a few important tricks that made the code so much easier to write (and much DRY-er).
One of the most important internals is the concept of
ChainedContraints. This is just a simple chain-of-responsibility collection of constraints, that will be lazily executed until one of them fail, in which case, it will be surfaced in order for part of the error message to be written by that failing constraint.
Another interesting trick has to do with the fact that operators are static constructs and, therefore, there is no way to represent them as an interface or a generic class. What one can do is, instead, use code generation to build a delegate that represents the operation. I used (and changed a bit) a working implementation from MiscUtil after remembering an article about generic numeric operations. Basically it uses expression trees to generate the delegates the execute the operators. Clever thing indeed.
So far, nothing else. Go get, use and give feedback Testing.Commons.NUnit from its Nuget home. And stay connected, if you will, for more of these contract checkers/verifiers.