Wednesday, November 17, 2010

C#: Opening Up For Mocking Unmockable Classes With The dynamic Keyword

I recently found myself in need of mocking the HttpContext class in an ASP.NET web application. The challenge is that HttpContext is sealed (can't inherit from it), doesn't inherit from anything that defines its interesting methods and is owned by the .NET API, so it can't be changed directly.

There is of course a traditional method of handling this situation, which is to write a very thin wrapper class that calls on the HttpContext methods and inherits from an interface for its own methods. While this class can't be tested directly, if every method is a simple passthrough method or property, just relaying parameters and results, it is a fairly safe class to have untested. And since it inherits from an interface, it's easy to make a mock of it for getting anything that is dependent on HttpContext under test.

Below is an example of such a wrapper class, making the SkipAuthorization property available for mocking.
public interface IHttpContextWrapper
{
    bool SkipAuthorization { get; set; }
}

public class HttpContextWrapper : IHttpContextWrapper
{
    public bool SkipAuthorization
    {
        get { return HttpContext.Current.SkipAuthorization; }
        set { HttpContext.Current.SkipAuthorization = value; }
    }
}
This is simple enough, but does add a layer to your project that doesn't have a natural fit, is there purely to facilitate testing, and contains more code to maintain.

However, with .NET 4.0 comes the dynamic keyword, which allows us to use some tricks that were earlier only available to dynamic languages, like Ruby or Python. So, if you're a programmer in a dynamic language, feel free to chuckle at us strongly typed language programmers finally getting it, and move on.

A common feature to dynamic languages is that you can match on behavior, rather than type. As such, if I have two classes with an .Execute() method, in a dynamic language they can be used interchangeably, so long as you're only using that method. This is true even if the two classes do not share a common parent of any sort.

The new 'dynamic' keyword in C# gives us this same ability, making available a new option for mocking away HttpContext or other difficult classes.

Take the following method. It's trivial and probably pointless since you can just check the HttpContext object directly, but it should be fine for illustrating the point.
public bool ShouldSkipAuthorization(HttpContext context)
{
    return context.SkipAuthorization;
}
Set up like this, all you can pass in is an HttpContext object. However, we can make the simple following change:
public bool ShouldSkipAuthorization(dynamic context)
{
    return context.SkipAuthorization;
}
Now I've replaced the HttpContext parameter type with the keyword dynamic. What this means is that instead of looking at the type of the object passed in, only the behavior will be considered. In other words, so long as the object passed in has a property called SkipAuthorization that returns a bool, it will run fine.

Changing the parameter to dynamic instead of HttpContext will work fine with the existing code. However, it also lets us do the following:
[TestFixture]
public class HttpContextHandlerTest
{
    [Test]
    public void WhenPassingFakeHttpContextResultIsValid()
    {
        var fakeContext = new FakeHttpContext { SkipAuthorization = true };
        var handler = new HttpContextHandler();
        var skip = handler.ShouldSkipAuthorization(fakeContext);
        Assert.IsTrue(skip);
    }
}

public class FakeHttpContext
{
    public bool SkipAuthorization { get; set; }
}
Here I've created a dummy class under my control that has a SkipAuthorization property. It is in no way related to HttpContext beyond having a property of the same name. Still, because of the dynamic keyword, I can pass this in and since everything matches, the code will execute, allowing me to mock out the HttpContext.

This is a very powerful tool, but there are some caveats.

As soon as you use the dynamic keyword, your method is no longer type safe. C# is a statically typed language, and the dynamic keyword gives you the option to bypass that completely. If you pass in an invalid object, you won't know that it crashes until it gets called at runtime. The loss of type safety is something that makes most strong-type programmers cower under their desks in fear.

However, with solid testing around it, and a sanity check in the method to check the incoming type and handle the failure gracefully if it doesn't match, the dynamic typing isn't so much a problem. Remember that people who work in dynamic languages write all their code that way, and it still somehow works.

Secondly, you lose all hints as to what types you actually should be passing in. You can no longer just look at the method signature to see what is expected. This can be sort of solved by adding a summary header to the method, but it feels hacky.

In conclusion, using the dynamic keyword lets you add testability without adding additional layers of abstraction that have no purpose in your code other than to add testability. It also means that you don't have untested wrapper code. What you lose is some code clarity and compile-time checking of type consistency.

Whether that's a fair tradeoff or not is up to you, but it is a fairly elegant way of sneaking in mock objects where there previously was a bunch of extra code required. In my own opinion, this can be a good viable middle stage while breaking up legacy code. However, I would want to get it back to static typing as soon as possible through redesigning the ugly code around it. I have nothing against dynamic coding in general, but I think it breaks a bit with the static typing paradigms that C# code is generally based on and may cause more confusion than it's worth for developers not familiar with the codebase.