The other day, I got a question from a fellow developer which I’ve heard asked many times before; “How do I mock X in my tests?”, where X is some difficult-to-mock class like the Linq to Sql DataContext or the ASP.NET HttpContext. The answer? Don’t!
Say you have some code like below (which is a silly simplified example, and not taken from any real-world app!):
public class ShoppingCartFinder : IFindShoppingCarts
{
public ShoppingCart FindCartForCurrentUser()
{
ShoppingCart cart = HttpContext.Current.Session["Cart"] as ShoppingCart;
if(null == cart)
{
// *snip* look for cart in database
}
return cart;
}
}
Testing this is hard, because of the tight coupling with the HttpContext class - you’d need to use something like TypeMock to be able to break this dependency (and it would be quite messy). The fact that you’re having difficulty testing your class, however, is a strong indication that you have a much more fundamental design problem at hand. As Udi mentions in a post today, design and testing really do go hand in hand. That’s why we sometimes refer to Test-Driven Development as Test-Driven Design; unit tests have an unique ability to not only ensure the correctness of our application, but also to drive the design using SOLID principles.
Had we used the dependency inversion principle when writing the code above, we would rather have something like this:
public class ShoppingCartFinder : IFindShoppingCarts
{
private readonly HttpContext _context;
public ShoppingCartFinder(HttpContext context)
{
_context = context;
}
public ShoppingCart FindCartForCurrentUser()
{
ShoppingCart cart = _context.Session["Cart"] as ShoppingCart;
if(null == cart)
{
// *snip* look for cart in database
}
return cart;
}
}
Okay, so now we have more loosely coupled code which no longer hides its dependencies. We’re still going to have trouble mocking the HttpContext though, same as before, because we’re programming against a concrete implementation rather than an abstraction. Here’s the thing though - fixing this is much easier than trying to mock the HttpContext!
public class ShoppingCartFinder : IFindShoppingCarts
{
private readonly ISessionCache _cache;
public ShoppingCartFinder(ISessionCache cache)
{
_cache = cache;
}
public ShoppingCart FindCartForCurrentUser()
{
ShoppingCart cart = _cache.Get<ShoppingCart>("Cart");
if(null == cart)
{
// *snip* look for cart in database
}
return cart;
}
}
public interface ISessionCache
{
T Get<T>(string key);
}
public class HttpContextSessionCache : ISessionCache
{
private readonly HttpContext _context;
public HttpContextWrapper(HttpContext context)
{
_context = context;
}
public T Get<T>(string key)
{
object value = _context.Session[key];
return value == null ? default(T) : (T) value;
}
}
By creating the abstraction and using the dependency inversion principle, we have a achieved a much clearer separation of concerns. This is also highlighted by using a much more intention revealing name on the interface. Now, all we need to do in our test is mock the ISessionCache interface, which is easy as pie even if you don’t want to use any mock framework.
So what’s the moral of this story? Step back and have a birds eye view at things when you encounter a problem like this – there might be a more fundamental design issue which, when solved, will make your lower-level problems go away by themselves.