On Removing HttpContext Dependencies

A faithful reader of the blog commented on my post the other day about using HttpRuntime vs HttpContext. Anderson's main point is absolutely right, and worth repeating. Here's what he said:

"You might consider refactoring your methods so that they don't have dependencies on context. If you have methods that depend on context to execute, you are not 'Unit' testing them, you are performing a functional test.

"It's a thin line and hard to keep straight, but basically your methods should be refactored to accept information that your presentation tier passes to them, rather than having them pull it straight out. Once you have done this, you can perform a "real" unit test that only tests that one unit of code, without dependencies on your presentation tier."

Having a dependency on the HttpContext in anything other than the UI makes the code much more difficult to test. There are ways of doing this by spinning up Cassini or even IIS in the background (ouch), but in general just keep HttpContext out of your BL and it will make testing a lot easier.

Of course there are two things I want to throw in here. First, we're doing this on an existing application. This is the second application I've worked on where we tried to retrofit the app for easy testing. At another job we failed to get adequate testing done on an application because of complete lack of support from upper management and whatnot. Fortunately at the current locale, we've got this being pushed by someone both smarter than me and with more clout than me, so we have a better chance of success :). Not only do we have HttpContext in our BL that we need to get out, we've got DL code in our UI code that bypasses the BL layer that we need to purge! If it was a new application, and we were building it for testability from the ground up, as I bet you know Anderson, it's a lot easier to build in looser coupling! It's refactor time...

Second, I would say that just about everything that you could get from HttpContext.Current via property should stop at the UI layer, with one exception: caching. It's made more sense to me to have the BL layer know caching and not the UI layer, so you need to be able to test the caching as a part of the BL testing. Even if one wants to make a strong distinction between "unit tests" and "functional tests", if you switch to HttpRuntime.Cache, you no longer have an external dependency (technically you do because it's in another dll, but you can depend on it being there, so it's not really a big dependency). In our case specifically (not to go into the details of how the guts of this application works), the BL has to know about the caching. So I think it makes sense here.

Comments

Anderson Imes (8/23/2007 9:46 AM)

Yes!  You are 150% correct about the caching API.  In fact, I think having put it in System.Web was a mistake.  It's useful for so many other applications other than web applications.  Scott Hanselman has a good summary of the agreement in the community about this issue.
http://www.hanselman.com/blog/UsingTheASPNETCacheOutsideOfASPNET.aspx

I also think that using the cache in your BL does not force you into using context in any way.  Even though you get a handle to cache from HttpRuntime, you aren't using context, so I think it's safe.

Testability sure is an interesting problem.  Especially adapting existing code to a more testable design.  It's a tough sell, too, depending on your management.

Thanks for the nice comments!

Eric (9/1/2007 9:53 AM)

Ditto...all the way.

David (10/11/2007 8:35 PM)

A complete lack of support from upper management for adequate testing? What job could you possibly be talk...oh, that's right. Nevermind.