Code Contract Preconditions in .NET 4.0

The .NET Framework version 4.0 contains a new collection of mechanisms for error detection which may impact the prestige of software testing. These mechanisms are contained in the System.Diagnostcs.Contracts namespace and fall into three categories: preconditions, postconditions, and invariants. I’ve been looking at preconditions first. As usual, some concrete examples are the best way to explain. Suppose you have a class which defines a mathematical combination:
 
public class MathCombination {
  public readonly long n;
  public readonly long k;
  public readonly long[] data;
  public MathCombination(long n, long k) {
    this.n = n;
    this.k = k;
    this.data = new
    for (long i = 0; i < k; ++i)
      this.data[i] = i;
  }
  // other methods here
}
 
The constructor doesn’t perform any argument checking. One traditional, non-Contracts way to perform error checking would be to use Debug.Assert such as:
 
public MathCombination(long n, long k) {
  Debug.Assert(n >= 0 && k >= 0, "Args must be non-negative");
  Debug.Assert(n >= k, "n must be greater or equal to k");
  …
}
 
This approach is most useful during development, and the Asserts are often removed from the retail build. Another traditional approach is if..then..throw:
 
public MathCombination(long n, long k) {
  if (n < 0 || k < 0)
    throw new Exception("Args must be non-negative");
  if (n < k)
    throw new Exception("n must be greater or equal to k");
  …
}
 
There is nothing wrong with this approach, however, automated tools cannot easily detect and deal with such error-checking. One way to check preconditions using Contracts is:
 
public MathCombination(long n, long k) {
  Contract.Requires(n >= 0 && k >= 0, "Args must be non-negative");
  Contract.Requires(n >= k, "n must be greater or equal to k");
  …
}
 
This approach will not automatically detect errors at runtime, but does allow you to optionally run the code through tools. One tool is a preprocessor called ccrewrite.exe which will create a new version of the executable which does in fact have runtime checks. Another tool is cccheck.exe which will analyze the source and attempt to prove that all contracts are met.
 
A second way to check preconditions using Contracts is:
 
public MathCombination(long n, long k) {
  Contract.Requires<System.Exception>(n >= 0 && k >= 0, "Args must be non-negative");
  Contract.Requires<System.Exception>(n >= k, "n must be greater or equal to k");
  …
}
 
It’s the same as the previous example except an Exception has been added. This approach requires the use of ccrewrite.exe, as opposed to the previous approach where ccrewite.exe is optional. A third way to check preconditions using Contracts is:
 
public MathCombination(long n, long k) {
  if (n < 0 || k < 0)
    throw new Exception("Args must be non-negative");
  if (n < k)
    throw new Exception("n must be greater or equal to k");
  Contract.EndContractBlock();
  …
}
 
This approach is the same as the traditional, non-contract if..then..throw approach but with a Contract.EndContractBlock() statement added. This allows tools to identify and treat traditional error-checking as contracts.
 
Well, there are a lot of implications here. Preconditions are nothing new, but in many situations developers can be negligent in their use of preconditions. Over the past few years, I have some informal survey evidence that the prestige of software testing has declined — this is a long story in itself. But I can speculate that one development scenario where testers could become hugely valuable is where developers write core code and a new breed of software testers insert all the contract code. 
 
Advertisements
This entry was posted in Software Test Automation. Bookmark the permalink.