Unit Testing ASP.Net Http Handlers and a discussion of Auto Mocking and the Testable pattern

7. December 2011 15:16 by Matt Wrock in   //  Tags:   //   Comments

This post was originally entitled Unit Testing ASP.Net Http Handlers and my intent was a quickie (5 minute) post on overriding ProcessRequest in a test friendly manner. Then, struck by diarrhea of the fingers, I found myself going off on unit testing practices and discussing a pattern I don’t hear a lot about but that provides a lot of value to me and the teams I work with. I hope you will find it of value too.

A test friendly IHttpHandler

The default Http Handler template that Visual Studio provides is not very Unit Test friendly. The key problem is here:

public void ProcessRequest(HttpContext context)
{

}

 

HttpContext is not your friend and it will eat your children if left unattended. Thankfully there is HttpContextBase who loves you and has a wonderful plan for your life. If you are not familiar with HttpContextBase, it provides everything that HttpContext has to offer wrapped up in mockable abstract classes.

Its really quite simple to transform the default visual studio HttpHandler template to a unit test friendly template. Use this instead:

using System.Web;

namespace RequestReduce.SassLessCoffee
{
public class IISHandler1 : IHttpHandler
{
public bool IsReusable
{
get { return true; }
}

public void ProcessRequest(HttpContext context)
{
ProcessRequest(new HttpContextWrapper(context));
}

public void ProcessRequest(HttpContextBase context)
{
//write your handler implementation here.
}

}
}

Now your unit tests can call ProcessRequest and pass in a mocked context which in turn will call the IHttpHandler contractual ProcessRequest.

As an example of a full fledged real world set of unit tests, I’ll use a handler that I am creating and will very soon release in my RequestReduce project which compiles .less dynamic css to plain old css.

Here is the Handler:

using System.Web;
using RequestReduce.Utilities;
using dotless.Core;
using dotless.Core.configuration;

namespace RequestReduce.SassLessCoffee
{
public class LessHandler : IHttpHandler
{
private readonly IFileWrapper fileWrapper;

public LessHandler(IFileWrapper fileWrapper)
{
this.fileWrapper = fileWrapper;
}

public void ProcessRequest(HttpContext context)
{
ProcessRequest(new HttpContextWrapper(context));
}

public void ProcessRequest(HttpContextBase context)
{
var localPath = context.Request.Url.LocalPath;
var response = context.Response;

try
{
var source = fileWrapper.GetFileString(localPath);

response.ContentType = "text/css";
response.Write(new EngineFactory(new DotlessConfiguration
{
CacheEnabled = false
}
).GetEngine().TransformToCss(source, localPath));
}
catch (System.IO.FileNotFoundException ex)
{
response.StatusCode = 404;
response.Write("/* File Not Found while parsing: " + ex.Message + " */");
}
catch (System.IO.IOException ex)
{
response.StatusCode = 500;
response.Write("/* Error in less parsing: " + ex.Message + " */");
}
}

public bool IsReusable
{
get { return true; }
}
}
}

As you can see there is not much going on here in my code thanks to the DotLess compiler. I simply want to expose my own handler instead of the DotLess handler which does pretty much the exact same thing but I turn off the caching since RequestReduce has its own. This handler will ship as part of my own Nuget package providing Sass and Coffee compiling as well that will automatically wire up the handler mapping configuration from within the core RequestReduse assembly.

There are really four things I want to test here:

  1. Make sure I am setting Content type to text/css.
  2. Make sure that I am successfully calling out to the compiler with .less and getting back css.
  3. Ensure I return a 404 if there is no .less file associated with the .less URL being called.
  4. Return a 500 if something wrong happens in the compile.

Here is my Test Class:

using System;
using System.IO;
using System.Web;
using Moq;
using RequestReduce.SassLessCoffee;
using RequestReduce.Utilities;
using Xunit;

namespace RequestReduce.Facts.SassLessCoffee
{
public class LessHandlerFacts
{
class TestableLessHandler : Testable<LessHandler>
{
public TestableLessHandler()
{

}
}

[Fact]
public void WillSetCorrectContentType()
{
var testable = new TestableLessHandler();
var context = new Mock<HttpContextBase>();
context.Setup(x => x.Request.Url)
.Returns(new Uri("http://localhost/RRContent/css.less"));
var response = new Mock<HttpResponseBase>();
response.SetupProperty(x => x.ContentType);
context.Setup(x => x.Response).Returns(response.Object);

testable.ClassUnderTest.ProcessRequest(context.Object);

Assert.Equal("text/css", response.Object.ContentType);
}

[Fact]
public void WillWriteCompiledLess()
{
var testable = new TestableLessHandler();
var context = new Mock<HttpContextBase>();
context.Setup(x => x.Request.Url)
.Returns(new Uri("http://localhost/RRContent/css.less"));
var response = new Mock<HttpResponseBase>();
context.Setup(x => x.Response).Returns(response.Object);
testable.Mock<IFileWrapper>().Setup(x => x.GetFileString(It.IsAny<string>()))
.Returns("@brand_color: #4D926F;#header {color: @brand_color;}");
var result = string.Empty;
response.Setup(x => x.Write(It.IsAny<string>())).Callback<string>(s => result = s);
const string expected = "#header {\n color: #4d926f;\n}\n";

testable.ClassUnderTest.ProcessRequest(context.Object);

Assert.Equal(expected, result);
}

[Fact]
public void WillReturn404IfFileNotFound()
{
var testable = new TestableLessHandler();
var context = new Mock<HttpContextBase>();
context.Setup(x => x.Request.Url)
.Returns(new Uri("http://localhost/RRContent/css.less"));
var response = new Mock<HttpResponseBase>();
context.Setup(x => x.Response).Returns(response.Object);
testable.Mock<IFileWrapper>().Setup(x => x.GetFileString(It.IsAny<string>()))
.Throws(new FileNotFoundException());
response.SetupProperty(x => x.StatusCode);

testable.ClassUnderTest.ProcessRequest(context.Object);

Assert.Equal(404, response.Object.StatusCode);
}

[Fact]
public void WillReturn500IfIOExceptionIsThrown()
{
var testable = new TestableLessHandler();
var context = new Mock<HttpContextBase>();
context.Setup(x => x.Request.Url)
.Returns(new Uri("http://localhost/RRContent/css.less"));
var response = new Mock<HttpResponseBase>();
context.Setup(x => x.Response).Returns(response.Object);
testable.Mock<IFileWrapper>().Setup(x => x.GetFileString(It.IsAny<string>()))
.Throws(new IOException());
response.SetupProperty(x => x.StatusCode);

testable.ClassUnderTest.ProcessRequest(context.Object);

Assert.Equal(500, response.Object.StatusCode);
}
}
}

Here are a few things I’ll call out here that may be of interest:

The three A’s: Arrange, Act and Assert

This is a common paradigm in unit testing and provides an initial and very basic way of framing your tests. Start with Arranging your tests, then Act by calling the method you are testing and lastly Assert that what you expect to be true (or false) really is as it should be. Now, for some reason the code snippet editor I use in Live Writer, my blog editor of choice, has decided that I really did not need the extra line breaks that I typically insert between my arrange, act, and assert code. So for clarity:

//ARRANGE
var testable = new TestableLessHandler();
var context = new Mock<HttpContextBase>();
context.Setup(x => x.Request.Url)
.Returns(new Uri("http://localhost/RRContent/css.less"));
var response = new Mock<HttpResponseBase>();
response.SetupProperty(x => x.ContentType);
context.Setup(x => x.Response).Returns(response.Object);

//ACT
testable.ClassUnderTest.ProcessRequest(context.Object);

//ASSERT
Assert.Equal("text/css", response.Object.ContentType);

XUnit and Moq are my TDD tools of choice

You will certainly note the using statements referencing these libraries at the top. Over the years I have used different tools for testing and mocking and these are the ones that have stuck because they are simple and just feel natural to me. I encourage you to try them as well.

Dependency Injection, the Testable pattern, and Auto Mocking

Dependency Injection

Both at work and here at play, I make heavy use of Dependency Injection. In short, this is a pattern whereby you inject services into a class often, but not always, via a constructor and usually in the form of interfaces or abstract classes that are wired up through an IOC container like StructureMap (what I use) or one of many other containers with similar quality and functionality. If you are not familiar with Dependency Injection (DI) or IOC (Inversion of Control) containers, please stop reading this and go to your favorite search engine (mine is google and, who are we kidding, so is yours) to find out more.

I use dependency injection for several reasons and one of those reasons is testability. By injecting the interfaces of services that I need I accomplish two (probably several more) things:

  1. The logic included in those services can be tested elsewhere in their own test classes and isolating them in this way allows me to focus just on the logic executed by the class I am currently testing.
  2. By using interfaces, I can easily mock the expected behavior of those services. This way I don’t have to call those services directly which would require me to possibly set up all sorts of other stuff like files and database connections (yuck).

This handler demonstrates an extremely simple use of DI:

private readonly IFileWrapper fileWrapper;

public LessHandler(IFileWrapper fileWrapper)
{
this.fileWrapper = fileWrapper;
}

My concrete implementation of IFileWrapper has all sorts of methods that do all sorts of fun and wonderful things with files. But who wants to deal with actual files in a unit test? I don’t.

The Testable Pattern

The Testable pattern, which I believe was coined by Brad Wilson (@bradwilson) co creator of XUnit, provides a nice structure for testing classes that use DI. Brad writes about it here. Essentially it involves creating a sort of wrapper class that derives from the actual class you are testing and exposes mocked, stubbed or faked implementations of its injectable services. The unit test class then works directly with this “Testable” class. A nice naming convention here is to call this testable class Testable[Your class under test name here]. Hence the class name I use: TestableLessHandler.

Auto Mocking

You may wonder why my testable class is so sparse. Well, they did not used to be. They used to look something like this:

class TestableLessHandler : LessHandler
{
public Mock<IFileWrapper> MoqFileWrapper { get; set; }

public TestableLessHandler(Mock<IFileWrapper> moqFileWrapper)
: base(moqFileWrapper.Object)
{
MoqFileWrapper = moqFileWrapper;
}

public static TestableLessHandler Create()
{
return new TestableLessHandler(new Mock<IFileWrapper>());
}
}

Well after writing about a hundred of these and often with many more than one dependency, I began thinking there has got to be a better way. And there is! Its called Auto Mocking, Something I have truly grown to love in an HR friendly sort of a way. My new testables look like this:

class TestableLessHandler : Testable<LessHandler>
{
public TestableLessHandler()
{
//place for default mock setups
}
}

I use StructureMap’s StructureMap.AutoMocking utility to accomplish this. You don’t have to use the full blown StructureMap IOC to use this. It is available as a separate Nuget package you can pull into your VS project and has a MoqAutoMocker class that works nicely with Moq. What auto mocking does is it looks at the dependencies that your class under test use and automatically creates Mocked implementations of the dependencies. Isn’t that nice?

I wrap up the auto mocking wire up in a class called Testable:

using System;
using StructureMap.AutoMocking;
using Moq;

namespace RequestReduce.Facts
{
public class Testable<TClassUnderTest> where TClassUnderTest : class
{
protected MoqAutoMocker<TClassUnderTest> autoMocker =
new MoqAutoMocker<TClassUnderTest>();

public Testable()
{

}

public Testable(Action<Testable<TClassUnderTest>> setup)
{
setup(this);
}

public Mock<TDependencyToMock> Mock<TDependencyToMock>()
where TDependencyToMock : class
{
var a = autoMocker.Get<TDependencyToMock>();
return Moq.Mock.Get(a);
}

public void Inject<T>(T type)
{
autoMocker.Inject(type);
}

public void InjectArray<T>(T[] types)
{
autoMocker.InjectArray(types);
}

public TClassUnderTest ClassUnderTest
{
get { return autoMocker.ClassUnderTest; }
}
}
}

This is the first class that I add to any test assembly I create. I cannot take credit for writing this class. I’m not sure exactly who wrote it but it came from a team lead by my colleague Tim Shakarian (aka tshak). This class uses a combination of StructureMap.AutoMock and Moq to allow me to create a testable instance of my class that automatically exposes all of its dependencies via Moq Mocks.

So my test method uses TestableLessHandler like so:

//Create the testable
var testable = new TestableLessHandler();
...
//Access my depencency via Mock method
testable.Mock<IFileWrapper>().Setup(x => x.GetFileString(It.IsAny<string>()))
.Returns("@brand_color: #4D926F;#header {color: @brand_color;}");
...

//call method I want to test via the ClassUnderTest property
testable.ClassUnderTest.ProcessRequest(context.Object);

After creating the testable, I can access its mocked dependencies via the Mock method. Not shown here, but something I often use, is the Inject method. I can say:

testable.Inject<IFileWrapper>(new SomeOtherFakeorRealFileWrapperImplementation())

When all the mock setups, callbacks, etc. are just as I want them to be and I’m ready to actually test my class, I can access the true class via the ClassUnderTest property.

Perhaps I’ll wrap up this testable into a Nuget package to make it easy for others (and me) to pull it into their test projects.

So hopefully this is useful to others. Ooops. spent too much time writing this and now I won’t be finishing my RequestReduce.SassLessCoffee Nuget package before work. Oh well…maybe tomorrow or tonight.

blog comments powered by Disqus

About Me

Hey thats me!

I'm Matt Wrock with over thirteen years of experience architecting scalable, distributed, high traffic web applications. I currently live in Woodinville, WA with my wife, two daughters, three dogs and cat. I work for Microsoft as a Sr. Software Engineer working in Cloud Developer Services. I'm also project founder and owner of http://www.requestreduce.org and a committer to http://chocolatey.org.

Month List