2008/08/25

Contract Verifiers - Part II. - The Exception Contract Verifier

Designing custom exceptions is not a so easy task. The recommended guidelines are relatively strict; and writing unit tests for them is fastidious. That is why the exception contract verifier is useful. It helps testing your custom exceptions, and catching potential design and behavioral problems.

The contract verifier is available as a companion attribute for your test fixture. It adds the necessary test methods, which verify that your exception is well designed and behaves as expected.
public class MyCustomException : Exception
{
// ...
}
using Gallio.Framework;
using MbUnit.Framework;
using MbUnit.Framework.ContractVerifiers;

[TestFixture]
[VerifyExceptionContract(typeof(MyCustomException))]
public class MyCustomExceptionTest
{
}
Although the test fixture contains no explicit test methods, the test runner will execute 5 tests provided by the contract verifier attribute. Each of them corresponds to a specific guideline:

1. Provides a public default parameter-less constructor.
2. Provides a public constructor taking one single parameter for the error message .
3. Provides a public constructor taking two parameters for the error message and an inner exception.

public class MyCustomException : Exception
{
public MyCustomException()
{
// ...
}

public MyCustomException(string message)
{
// ...
}

public MyCustomException(string message, Exception innerException)
{
// ...
}
}
If for some reason, you decide that your custom exception should handle with construction differently, you can disable the 3 tests above by setting to false the named parameter ImplementsStandardConstructors.
[TestFixture]
[VerifyExceptionContract(typeof(MyCustomException), ImplementsStandardConstructors = false)]
public class MyCustomExceptionTest
{
}
4. Decorates your custom exception with the attribute System.SerializableAttribute.
[Serializable]
public class MyCustomException : Exception
{
}
5. Implements a protected constructor for data serialization.
public class MyCustomException : Exception, ISerializable
{
protected MyCustomException(SerializationInfo info, StreamingContext context)
{
// ...
}
}
Again, in some scenarios, you might decide to not support the serialization process (if your code base targets the compact framework, for instance). Disable the serialization tests by setting to false the named parameter ImplementsSerialization.
[TestFixture]
[VerifyExceptionContract(typeof(MyCustomException), ImplementsSerialization = false)]
public class MyCustomExceptionTest
{
}
When the serialization tests are enabled, the test runner will verify the preservation of data during a complete round trip serialization operation. This will be done for all the constructors; including the 3 recommended standard constructors described above.

The following sample shows the complete code for a general-purpose custom exception; and the test fixture verifying it.
[Serializable]

public class MyCustomException : Exception, ISerializable
{
public MyCustomException()
{
}

public MyCustomException(string message)
: base(message)
{
}

public MyCustomException(string message, Exception innerException)
: base(message, innerException)
{
}

protected MyCustomException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}

public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
}
}
[TestFixture]
[VerifyExceptionContract(typeof(MyCustomException))]
public class MyCustomExceptionTest
{
}
And here is the report generated by Gallio for that test fixture:

2008/08/19

Contract Verifiers - Part I. - Introduction

Writing unit tests is costly. Surely less costly than not writing them, but costly too. A commonly accepted trade-off consists in not testing trivial features, or worst, repeating features. You know, all those dozens of little classes that implement repeatedly a roughly similar stuff: custom exceptions, event arguments, equatable, comparable, or disposable interfaces, etc. If you write unit tests, you know how painful and uninteresting it is to test those objects.

Honestly, look at the last project you have worked on. Do you see all those custom exceptions you have written? Have you really tested their serialization and unserialization support? Look at these classes implementing the generic IEquatable interface. Have you verified that the equality behaves correctly in all the possible ways (to a null reference, to itself, commutability of the operation, etc?) If the answer is no, then I sympathize. If yes, well, it was a so boring job, wasn't it?



The next release of Gallio (alpha 4) will come with a cool feature called Contract Verifiers. Actually, it is already available in the alpha 3, but under a preliminary form which will be probably marked as obsolete in the future (or perhaps simply removed).
[Update; Actually, Jeff has just removed it from the repository.]

The principle of the contract verifiers is simple. Basically, a contract verifier is a set of predetermined test methods that you can attach to a given test fixture without actually writing them (we could describe it as a kind of Mixin). The test methods will verify for you that the object fulfills the given implementation contract. It is an all-in-one functionality because the test methods have already been written for you. They also support some configuration; so they can be used in different scenarios.

Let's take an example. Imagine you need to write a class A implementing a well-known interface, or deriving from a well-known base class. Let's call it X. You will have to override the necessary members of X, so that A works correctly; and to write a bunch of unit tests that will verify that the functionalities of X (let's call them x1, x2 and x3) work as expected in your implementation of A. Fortunately, Gallio comes with a contract verifier for X. We will name it XContractVerifier. Unit testing A comes suddenly easy:

[TestFixture]
[TestsOn(typeof(A)]
[XContractVerifier(option1=blabla, option2=blabla)]
public class ATest
{
// My other own tests here...
}

Thanks to the XContractVerifier attribute, and during the execution of the test fixture, the test runner of Gallio will add behind the scene, the necessary test methods that will verify that A implements correctly x1, x2, and x3.

Gallio Alpha 4 includes three built-in contract verifiers (it is a beginning):
It is interesting to notice that the internal working of the contract verifiers is based on the remarkable capability of the Gallio exploration engine to dynamically attach child test methods to a given fixture; although those test methods do not physically exist.