views:

104

answers:

2

I’m relatively new to testing and MVC and came across a sticking point today. I’m attempting to test an action method that has a dependency on HttpContext.Current.Cache and wanted to know the best practice for achieving the “low coupling” to allow for easy testing. Here's what I've got so far...

 public class CacheHandler : ICacheHandler
 {
    public IList<Section3ListItem> StateList
    {
        get { return (List<Section3ListItem>)HttpContext.Current.Cache["StateList"]; }
        set { HttpContext.Current.Cache["StateList"] = value; }
    }
 ...

I then access it like such... I'm using Castle for my IoC.

public class ProfileController : ControllerBase
{
    private readonly ISection3Repository _repository;
    private readonly ICacheHandler _cache;

    public ProfileController(ISection3Repository repository, ICacheHandler cacheHandler)
    {
        _repository = repository;
        _cache = cacheHandler;
    }

    [UserIdFilter]
    public ActionResult PersonalInfo(Guid userId)
    {
        if (_cache.StateList == null)
            _cache.StateList = _repository.GetLookupValues((int)ELookupKey.States).ToList();
   ...

Then in my unit tests I am able to mock up ICacheHandler.

Would this be considered a 'best practice' and does anyone have any suggestions for other approaches?

Thanks in advance.
Cheers

+1  A: 

You are hiding a specific, hard-to-test API (HttpContext.Current) behind and interface and using Constructor Injection to inject the dependency into the consumer. That's more or less textbook DI (I would add Guard Clauses in the constructor, though).

If you create a new ASP.NET MVC project in Visual Studio, you will see that in the AccountController.cs file, a very similar thing is being done to hide the MembershipProvider.

Mark Seemann
+1  A: 

The recommended approach is to stub HttpContextBase. Its documentation states

When you perform unit testing, you typically use a derived class to implement members with customized behavior that fulfills the scenario you are testing.

This is mostly covered for TypeMock here.

var httpContext = MockRepository.GenerateStub<HttpContextBase>();
httpContext.Stub(x=>x.Cache).Return(yourFakeCacheHere);

var controllerContext = new ControllerContext(httpContext, ....);

var controller = new HomeController();
controller.ControllerContext = controllerContext;
gWiz