views:

64

answers:

4

ASP.NET MVC has come a long way (compared to webforms) in becoming a unit testable framework. However, we are often faced with some remaining pigs like FormsAuthentication, which I usually wrap in some type of UserSession object to keep it clean and testable. The other day I realized I was using Server.MapPath in my controller action, and while things like MvcContrib make it easy(ish) to mock the current request, I found myself going down the path of creating a FileSystemService class to wrap operations with the file system. The benefits are that you get a tight API exposing just the methods you need, and it's easy to stub/mock in tests. The downside is that it is yet another constructor dependency.

What does the SO community thing about this situation? Where do you draw the line when trying to make your controllers as light and flexible as possible?

+1  A: 

I would generally draw the line at the point where you need to actually get something done (i.e. somebody wants to use the code you're writing). If it takes another half a day to write your service etc. and your deadline is looming, then some things are best left...

Paddy
A: 

Bear in mind that ASP.NET MVC has already replaced the sealed HttpContext with more test-friendly HttpContextBase, which you can mock to provide your tests with your chosen implementations of Server.*, Request.*, Response.*, etc.

You just need to set the controller's ControllerContext to provide your test context:

controllerUnderTest.ControllerContext = new ControllerContext( testHttpContext, new RouteData(), controllerUnderTest );
stevemegson
A: 

This is a great question! I do the same thing you do - wrap HttpContext dependencies so they can be visible, inverted, and mocked.

I draw the controller line at validating the post values and saving or passing the call off to a task layer. So if I have to validate, save some data, send an email to marketing if a preferred customer makes a purchase, log something, etc., that's too much. I'd put that in a task layer and have my controller call that task layer. Most of the dependencies then float to the task layer constructors and come out of the controller.

My general rule of thumb is this: If you were asked to create a Silverlight or some other UI on your app, would you be totally rewriting it? Controllers should stick to presentation logic. If you can scrape off the presentation layer and put another one on, I think that gives you the best future proofing.

Of course, where you draw the line is up to you and your app. See Who Can Help Me, which is based on Sharp Architecture, for more about the task layer and skinny controllers.

Joe Wilson
A: 

There are also these wrappers around things like HttpContext to make testing easier:

http://www.codethinked.com/post/2008/12/04/Using-SystemWebAbstractions-in-Your-WebForms-Apps.aspx

So to sum it up:

Get System.Web.Abstractions

Use HttpContext base as a parameter, not HttpContext:

public void SomeMethod(HttpContextBase context)
{
}

Call the method wrapping your real http context

HttpContextBase contextBase = new HttpContextWrapper(context);
SomeMethod(contextBase);

You can provide fakes like so (example using Moq):

var context = new Mock<HttpContextBase>();
context.Setup(x => x.Request.QueryString).Returns(new NameValueCollection { { "Id", "5" } });
CRice