0

I need to setup a policy in base controller that applies to all controller instance, like below:

  public class BaseController : Controller
    {

        private IPolicy Policy;

        public BaseController()
        {
            this.Policy= new Policy(HttpContext);
        }
    }

Within the Policy class, I need to do something like:

   this.httpContextBase.User.

Questions: (Update)

What is the better way to design the BaseController in terms of using HttpContext and Unit test.

Pingpong
  • 7,681
  • 21
  • 83
  • 209
  • 1
    Well, for one thing, it's ideal if you use [global](http://weblogs.asp.net/gunnarpeipman/archive/2010/08/15/asp-net-mvc-3-global-action-filters.aspx) [action filters](http://msdn.microsoft.com/en-us/library/gg416513(v=vs.98).aspx) rather than [a common base class](http://stackoverflow.com/questions/6119206/what-are-good-candidates-for-base-controller-class-in-asp-net-mvc). – Kirk Woll May 26 '12 at 00:28
  • Still, in unit test, those actionmethod is not invoked. Thus, Policy is not initialized. – Pingpong May 26 '12 at 08:47

1 Answers1

6

What is the correct way to unit test HttpContext?

Absolutely no way. You are using the HttpContext inside the constructor of a controller when this context is still not initialized. Not only that this code cannot be tested but when you run the application it will also crash with NRE. You should never use any HttpContext related stuff in a constructor of a controller.

One possibility is to refactor your code and perform this inside the Initialize method:

public class BaseController : Controller
{
    private IPolicy Policy;

    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);
        this.Policy = new Policy(HttpContext);
    }
}

This being said, that's not the approach I would recommend. I would recommend you using dependency injection instead of service location which is considered by many as an anti-pattern.

So:

public abstract class BaseController : Controller
{
    protected IPolicy Policy { get; private set; }

    protected BaseController(IPolicy policy)
    {
        Policy = policy;
    }
}

Now, all that's left is to configure your favourite Dependency Injection framework to inject the correct instance into the constructor. For example with Ninject.Mvc3 this is achieved with a single line of code:

kernel.Bind<IPolicy>().To<Policy>();

Now you can feel more than free to mock this IPolicy in your unit test without even caring about any HttpContext.

For example let's suppose that you have the following controller that you want to unit test:

public class FooController : BaseController
{
    public FooController(IPolicy policy): base(policy)
    { }

    [Authorize]
    public ActionResult Index()
    {
        Policy.DoSomething();
        return View();
    }
}

Now, all that you need to do is pick up your favorite mock framework (Rhino Mocks in my case) and do the mocking:

[TestMethod]
public void Index_Action_Should_DoSomething_With_The_Policy()
{
    // arrange
    var policyStub = MockRepository.GenerateStub<IPolicy>();
    var sut = new FooController(policyStub);

    // act
    var actual = sut.Index();

    // assert
    Assert.IsInstanceOfType(actual, typeof(ViewResult));
    policyStub.AssertWasCalled(x => x.DoSomething());
}
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Within Initialize method, the HttpContext is not null, but the method is NOT invoked when unit-tested (instantiation of BaseController's child class). Thus, exception of "reference is not set to an object" when Policy field is accessed – Pingpong May 26 '12 at 08:50
  • @Pingpong, sorry my answer was incomplete. I forgot to actually provide the correct way to design this. – Darin Dimitrov May 26 '12 at 10:07
  • On Dependency Injection, the Policy constructor expects HttpContextBase parameter, will IoC container be able to inject it. I am using Unity IoC? Thanks for the advice! – Pingpong May 26 '12 at 10:38
  • I don't know about Unity, but Ninject does this automatically. – Darin Dimitrov May 26 '12 at 11:05
  • By refactoring the BaseController, is the HttpContext NOT NUll? The idea is that the IPolicy should be changed via BaseController only, but now every controller might assign a different type of IPolicy. – Pingpong May 26 '12 at 11:23
  • Mark this as answer based on Darin's efforts. However, having to specify policy for all controller is not an ideal solution. Eventually, I encapsulate the logic of policy within basecontroller. – Pingpong Jun 01 '12 at 22:54