The point of unit testing is to isolate away any dependencies. Otherwise you are not unit testing, you are integration testing.
The problem
So how do you isolate away your dependencies so you are only testing the code of interest? Well let's take a look.
Consider the following example controller/action:
class MyController
{
public ActionResult MyAction()
{
if (HttpContext.Current.Request.RawUrl.Contains("SomeString")
{
return Redirect("/Someurl");
}
return View();
}
}
This is terrible code for a number of reasons (model binding, anyone?)... but for the present discussion, set that aside, and just note that HttpContext
is created by the action method via a sort of singleton pattern. It's not injected, and there is no way to change it, other than to modify your code, or do something really weird with the .NET profiling API, like TypeMock does. Without heroic efforts, you are stuck using the real HttpContext object in the .NET runtime.
Now imagine you want to write a unit test to see if the action method does indeed perform a redirect when the URL contains "SomeString". how would you do it? Where do you get the HttpContext? You need a real one. So do you spin up a web server on your unit testing machine? Invoke it with a web client? Will you need to sign on? Create cookies? Ugh.
Bottom line: You have no way to isolate away the dependency on HttpContext.
A better way
If we were to modernize this with a bit of DI, it might look like this:
class MyController
{
private readonly HttpContextBase _httpContext;
public MyController(HttpContextBase context) //Injected
{
_httpContext = context;
}
public ActionResult MyAction()
{
if (_httpContext.Request.RawUrl.Contains("SomeString")
{
return Redirect("/Someurl");
}
return View();
}
}
Now we can test this, by supplying a mockup of the HTTP context that conforms to HttpContextBase's interface, like this:
class MockHttpRequest: System.Web.HttpRequestBase
{
public override string RawUrl
{
get
{
return "SomeString";
}
}
}
class MockHttpContext : System.Web.HttpContextBase
{
public override System.Web.HttpRequestBase Request
{
get
{
return new MockHttpRequest();
}
}
}
And in your unit test code:
//Arrange
var context = new MockHttpContext();
var controller = new MyController(context);
//Act
var result = controller.MyAction();
//Assert
Assert.IsTrue(result is RedirectResult);
Piece of cake. Don't need a web server. Don't even need Moq or TypeMock or Fakes. You can do it all yourself. It would not only be harder, but it would be impossible the traditional way, without a mocking framework.
P.S.
Incidentally, Microsoft thought this was so valuable they added HttpContextBase
just so that it could be injected and overridden in unit tests (the regular HttpContext
is sealed). If they went to the trouble to add it to the runtime, it's probably worth using.