Complex Assertions in C#

Recently I attempted to implement a declarative predicate checking system to allow design by contract (DBC) within C# 3.0. I was not successful due to a limitation in the kind of parameters one can pass to an Attribute constructor in .NET (no lambdas). I thought I’d just follow that up with a simpler model based on extension methods.

public static class Predicates
{
    public static void Assert<T>(this T obj, Func<T, bool> pred)
    {
        if (!pred(obj))
            throw new ApplicationException();
    }
}

This simple extension method can be attached to any object allowing Ensures and Requires like this.

int MyIntProp{get;set;}

public void MyMethod()
{
    this.Assert(x => x.MyIntProp < 10);
    MyIntProp += 10;
    this.Assert(x => x.MyIntProp >= 10);
}

This is a nice clear implementation that is good for validation. But I think that I can extend it further by exploiting serialization of snapshots within a scope to allow before/after analysis within the scope. Here’s what I want to be able to write:

public void MyBetterMethod()
{
    this.Require(x => x.MyIntProp < 10);
    MyIntProp += 10;
    this.Ensure(x => x.MyIntProp == x.before().MyIntProp + 10);
}

Well, my recent writings about the Ambient Context pattern might give you a clue about how I would manage the scope. The first thing I need to be able to do is store a snapshot of the object before it gets tested by the Require. I chose an IDisposable object so that I can clean up after myself without the danger of having the serialized guts of objects lying around everywhere.

public class PredicateScope : IDisposable
{
    [ThreadStatic]
    public static Stack<PredicateScope> Scopes = 
        new Stack<PredicateScope>();
    internal readonly Dictionary<object, string> Snapshots = 
        new Dictionary<object, string>();
    internal readonly Dictionary<object, object> DeserializedSnapshots = 
        new Dictionary<object, object>();

    public PredicateScope(params object[] objects)
    {
        foreach (object obj in objects)
        {
            Snapshots.Add(obj, CreateSnapShot(obj));
        }
        Scopes.Push(this);
    }

    static string CreateSnapShot(object obj)
    {
        XmlSerializer serializer = new XmlSerializer(obj.GetType());
        StringWriter sr = new StringWriter();
        serializer.Serialize(sr, obj);
        return sr.ToString();
    }

    public void Dispose()
    {
        Snapshots.Clear();
        Scopes.Pop();
    }
}

You just pass the scope object whatever objects you intend to test later on. It takes snapshots of the objects and stores them away for later reference. It also maintains a stack, so it can be nested. Strictly speaking this is unnecessary, but I figure it might come in handy later on.

My Assertion methods are pretty much the same, but they’re now augmented by a “before” extension method that will get a snapshot keyed to the object it’s extending, and return that instead.

public static class Predicates
{
    public static void Require<T>(this T obj, Func<T, bool> pred)
    {
        if (!pred(obj))
            throw new ApplicationException();
    }

    public static void Ensure<T>(this T obj, Func<T, bool> pred)
    {
        if (!pred(obj))
            throw new ApplicationException();
    }

    public static T before<T>(this T obj) where T : class
    {
        if (obj == null)
            throw new ArgumentNullException("obj cannot be null");

        PredicateScope ctx = PredicateScope.Scopes.Peek();
        if (ctx == null) return default(T);

        if (ctx.DeserializedSnapshots.ContainsKey(obj))
            return ctx.DeserializedSnapshots[obj] as T;
        string serializedObject = ctx.Snapshots[obj];
        XmlSerializer ser = new XmlSerializer(typeof(T));
        XmlReader reader = XmlReader.Create(new StringReader(serializedObject));
        object result = ser.Deserialize(reader);
        ctx.DeserializedSnapshots[obj] = result;
        return result as T;
    }
}

The before method gets the snapshot out of the scope, and returns that. You can then use it in your assertions in exactly the same way as the original object.

[TestFixture, DataContract]
public class MyClass
{
    [DataMember]
    public int MyInt { get; set; }
    [Test]
    public void MyMethod()
    {
        using (new PredicateScope(this))
        {
            this.Require(x => x.MyInt < 10);
            MyInt += 10;
            this.Ensure(x => MyInt == x.before().MyInt + 10);
        }
    }
}

Obviously, for production use you’d have to ensure this stuff didn’t get run by using ConditionalAttribute. It would affect performance. But for debugging it can be a godsend.

Advertisement