.NET Code Contracts and TDD Are Complementary

After my recent post on the introduction of Code Contracts in .NET 4.0, I got some strong reaction that they would rather rely on TDD, or the better term, Example Driven Development specifications to ensure correctness.  Instead, it's my intent to talk about how they can complement each other in some rather powerful ways, such as having contracts bound to interfaces instead of class instances to ensure uniform interaction.  When we combine these two things together, the edge cases of our behaviors quickly melt away.

 

Design by Contract Basics

In order to understand how it can help, it's best to understand more about what Design by Contract can do for us.  The basic idea revolves around three questions:

  • What does it expect? (Pre-conditions)
  • What does it guarantee? (Post-conditions)
  • What does it maintain? (Invariants)

Now, to map that to actual software terms, these constraints are enforced at the method level.  Listed below are the basic rules for how contracts work:

  • Pre-conditions may be weakened and not strengthened by subclasses
  • Post-conditions may be strengthened and not weakened by subclasses
  • Invariants may be strengthened and not weakened by classes

The intent of DbC and using contracts, your program must never try to verify the contract conditions and instead fail hard, with the contract verification being your safety net.  This fail hard technique makes reading and understanding this code easier because the intended behavior is clearly specified.  When using the .NET Code Contracts, these hard failures are enforced either through the Debug.Assert or the Environment.FailFast implementations, depending on the build configuration.

 

TDD and Contracts Strategies

We have a couple of options when we talk about Code Contracts and TDD.  Either way, the tests always come first.  Then what comes next is up to interpretation.

We have the option of:

  1. Defining the contract implementation after the test, to narrow the behavior of the code under test, then write the code required to pass. 
  2. Defining the code to achieve a passing test, then refactor the code to a contract implementation.

It really depends on the application as to which style might work better.  For most applications, I would recommend that you go with the second option where you define the code first to pass, and then refactor to an evolving contract design.  For those creating frameworks with predefined behaviors, the first option would probably work best.

One thing that Code Contracts gives us is the ability apply intrinsic behavior to interfaces.  At that point, I no longer really call them interfaces, but abstract contracts is a better term for them.  I favor their use as much as possible to achieve uniform behavior across implementations and really becomes a powerful tool to enforce Don't Repeat Yourself (DRY).

 

Red - Yellow - Green Refactor

One analogy that can be adapted here is the original idea of Red, Green, Refactor.  Let's take this a step further to use a phrase that Greg Young uses, which is Red, Yellow, Green, Refactor.  This is to say that we first implement the test, fail with a red light because no code actually exists for it to compile or work.  Then we implement the contract and our code to a point where it can at least compile, which would be yellow.  After that, we then pass our test with green after implementing the right amount of code to satisfy our contract.  We can refactor our contracts and code to clean it up a bit more, and move onto the next test.

 

BDD for the Business and DbC for the Minutia

Another argument that can be made is that we could make is that we treat BDD for the business and Design by Contract for the minutia.  This idea is that we can continue to use the BDD syntax that we normally do, and then use contracts to enforce the edge constraints, to narrow our definitions.  This would allow us to focus on the business at the high level using BDD, and then the low level enforcement using Code Contracts.

 

Domain Driven Design and Design by Contract

One of the topics mentioned in the hallmark book Domain Driven Design, Eric Evans covers Design by Contract in Chapter 10 on Supple Design.  In this chapter, he states:

State post-conditions of operations and invariants of classes and aggregates.  If assertions cannot be coded directly into your programming language, write automated unit tests fort them.  Write them into documentation or diagrams where it its the style of the project's development status.

Seek models with coherent sets of concepts, which lead a developer to infer the intended assertions, accelerating the learning curve and reducing the risk of contradictory code.

What is interesting is that he talks about putting assertions into our code.  Given that Code Contracts are available to us to use, we can take advantage of exactly what he states.  These contracts then become part of the documentation of our code, with an intention revealing interface of what the behavior of this code is.

Further, he states:

Even though many object oriented languages don't currently support assertions directly, assertions are still a powerful way of thinking about a design.  Automated unit tests can partially compensate for lack of language support.  Because assertions are all in the term of states, rather than procedures, they make tests easy to write.  The test setup puts the preconditions in place; then, after execution, the tests check whether the post-conditions hold.

Clearly stated invariants and pre- and post-conditions allow for a developer to understand the consequences of using an operation or object....  so it's important to find models that make sense to people as well as satisfying the needs of the application.

This states the case rather succinctly for using code contracts when the language supports them.  Indeed, to be able to statically enforce these constraints and prove their success or failure is key.  Enough though of the talk about DbC and Domain Driven Design, let's actually walk through an example of how to work these in conjunction together.

 

Walking Through an Example

Let's walk through a simple example to demonstrate how these two may work together.  In this example, we will define an immutable generic list that is optimized for recursion.  This implementation would be fairly similar to an implementation done by the F# team as part of the immutable collections.  I'll be going into that a bit later in a different post.

The first example we want to give is for an empty list.  For the success criteria for this example, we will say that for a given empty list, an is empty flag should be true, the length is 0, and there should be exceptional behavior for checking the list head and tail.

[Fact]
public void EmptyList_ShouldBeEmpty()
{
    // Act
    IImmutableList<int> list = ImmutableList<int>.Empty;

    // Assert
    Assert.True(list.IsEmpty);
    Assert.True(list.Length == 0);
    Assert.Throws<ArgumentException>(
        () => { var i = list[0]; });
    Assert.Throws<InvalidOperationException>(
        () => { var head = list.Head; });
    Assert.Throws<InvalidOperationException>(
        () => { var tail = list.Tail; });
}
 

Now that the empty list example has been defined, let's actually work to implement the behavior through the use of a contract.  We're going to follow the first option enumerated above on defining a contract first, and then create the code to implement the feature, because this is more of a framework piece than it is for any standard line of business application code.  In order to create code contracts on interfaces, we need to create a ContractClass that contains our contracts and decorate our interface for it.  I will have to define later a class that implements the code contract class.

In my test above, I need to define a few items on my interface for success, which is an empty flag, a length, the head and the tail of the list.  Below is the definition of this interface.

[ContractClass(typeof(IImmutableList_Contract<>))] 
public interface IImmutableList<T> : IEnumerable<T> 

    bool IsEmpty { [Pure] get; } 
    int Length { [Pure] get; } 
    T Head { [Pure] get; } 
    IImmutableList<T> Tail { [Pure] get; } 
    T this[int index] { [Pure] get; } 

 

Once this is defined, we can now create our contract class.  This must implement the IImmutableList<T> interface as well as mark it with an attribute stating that it is the contract class for the given interface.  Each method in turn must be implemented with their given pre-conditions, post-conditions and invariants.  Each method must also return the result from the comutation by using the CodeContract.Result<T>() method. 


[ContractClassFor(typeof(IImmutableList<>))]
internal sealed class IImmutableList_Contract<T> : IImmutableList<T>
{
    public IEnumerator<T> GetEnumerator()
    {
        CodeContract.Ensures(IsEmpty ?
            null == CodeContract.Result<IEnumerator<T>>() :
            null != CodeContract.Result<IEnumerator<T>>());
        return CodeContract.Result<IEnumerator<T>>();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public bool IsEmpty
    {
        [Pure]
        get { return CodeContract.Result<bool>(); }
    }

    public int Length
    {
        [Pure]
        get
        {
            CodeContract.Ensures(0 <= CodeContract.Result<int>());
            return CodeContract.Result<int>();
        }
    }

    public T Head
    {
        [Pure]
        get
        {
            CodeContract.Requires(!IsEmpty);
            return CodeContract.Result<T>();
        }
    }

    public IImmutableList<T> Tail
    {
        [Pure]
        get
        {
            CodeContract.Requires(!IsEmpty);
            return CodeContract.Result<IImmutableList<T>>();
        }
    }

    public T this[int index]
    {
        [Pure]
        get
        {
            CodeContract.Requires(!IsEmpty);
            CodeContract.Requires(0 <= index && Length < index);
            return CodeContract.Result<T>();
        }
    }
}
 

Now there is enough code to satisfy my preconditions.  Moving onto the actual implementation, there is a bit of code required to get this code to pass.   Let's implement the bare bones code that would be required to get the test to pass.

public abstract class ImmutableList<T> : IImmutableList<T>
{
    public class _Empty : ImmutableList<T> { }

    public class _Cons : ImmutableList<T>
    {
        public T _Head { get; internal set; }
        public ImmutableList<T> _Tail { get; internal set; }

        internal _Cons(T head, ImmutableList<T> tail)
        {
            _Head = head;
            _Tail = tail;
        }
    }

    private static IImmutableList<T> empty =
        new _Empty();
    public static IImmutableList<T> Empty 
    { get { return empty; } }

    public bool IsEmpty
    { [Pure] get { return this is _Empty; } }

    public int Length
    { [Pure] get { return 0; } }

    public T Head
    { [Pure] get { return ((_Cons)this)._Head; } }

    public IImmutableList<T> Tail
    { [Pure] get { return ((_Cons)this)._Tail; } }
       
    public this[int index]
    { [Pure] get { return default(T) } }
}
 

Now that we got enough code to compile, we have another problem when we run the tests?  What is it?  Well, our exceptional cases go away, because the system will fail hard should you violate them by either calling Debug.Assert or Environment.FailFast.  Therefore, the test needs to be rewritten slightly to leave off those exceptional cases.

[Fact] 
public void EmptyList_ShouldBeEmpty() 

    // Act 
    IImmutableList<int> list = ImmutableList<int>.Empty; 

    // Assert 
    Assert.True(list.IsEmpty); 
    Assert.True(list.Length == 0); 
}
 

Now the test, as written should compile and succeed.  What we have gained here is quite a bit.  With static proving, and the ability to encapsulate behavior in our interfaces, we have saved ourselves from DRY for subsequent implementations of this interface.  Given the success of this, we are now able to move on to our next test which is when we have items in the list, we should have a head, a tail, not empty and a length of 10.

 

[Fact]
public void NonEmptyList_ShouldShowProperCount()
{
    // Arrange
    var range = Enumerable.Range(1, 10);

    // Act
    IImmutableList<int> list = 
        ImmutableList<int>.FromIEnumerator(range.GetEnumerator());

    // Assert
    Assert.True(list.Length == 10);
    Assert.False(list.IsEmpty);
    Assert.True(list[0] == 1);
    Assert.True(list.Head == 1);
    Assert.True(list.Tail != null);
}
 

As we define more tests or examples as we call them, we further refine the contract to include the proper bounds checks and add more functionality to the interface and to the list implementation.  What's nice about this implementation is that our contracts don't litter our code, but instead are enforced at the interface level.  That's not to say that we can't later on add more invariants, pre-conditions and post-conditions to our class implementations itself instead of the interface level.

 

Checked Exceptions All Over Again?

Some people may look at this and start to wonder if this is checked exceptions all over again.  It's not for some simple reasons.  First off, the code contract implementation does not force you to handle the exceptions in any way.  Instead, when this becomes part of the documentation, it states what the pre-conditions, post-conditions, and invariants are, and during compilation, they will be displayed back as warnings when you have violated the contracts, as well at runtime as hard failures.  In the Java world, you are forced to handle the exceptions, even if you met the pre-conditions, which isn't very helpful.

 

Wrapping It Up

We have covered quite a bit here today.  I hope this overview can give you a brief understanding of how Design by Contract can work in conjunction with TDD to produce verifiable code.  Given the ability to statically verify our contracts and behavior is important as our contracts then become part of the documentation for our system.  In the next post in this series, we're going to talk more about guidance on where you should compromise and where you should never compromise when it comes to Code Contracts.

In the mean time, as always, download the latest version of the .NET Code Contracts and give the team feedback.



kick it on DotNetKicks.com

1 Comment

  • Не вижу причиныНе вижу причины.expandAll() приводит в конце концов к paintEvent, и так делать, как я тут написал, не стоит. Конечно, я готов допустить, что возможна реализация expandAll(), которая проверяет необходима ли ее работа и, если не необходима, то не делать ничего, что могло бы привести к paintEvent.Однако, Троллтех в Qt 4.5.0 выразился так:void QTreeView::expandAll(){ Q_D(QTreeView); d->viewItems.clear(); d->expandedIndexes.clear(); d->interruptDelayedItemsLayout(); d->layout(-1); for (int i = 0; i viewItems.count(); ++i) { if (d->viewItems[i].expanded) cnuoitne; d->viewItems[i].expanded = true; d->layout(i); QModelIndex idx = d->viewItems.at(i).index; d->expandedIndexes.insert(idx.sibling(idx.row(), 0)); } updateGeometries(); d->viewport->update();}Последние строчки не оставляют шанса посту.

Comments have been disabled for this content.