使用HttpContext,控制器设计初始化BaseController的字段

时间:2012-05-26 00:21:23

标签: asp.net-mvc unit-testing design-patterns architecture

我需要在基本控制器中设置适用于所有控制器实例的策略,如下所示:

  public class BaseController : Controller
    {

        private IPolicy Policy;

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

在Policy类中,我需要执行以下操作:

   this.httpContextBase.User.

问题:(更新

在使用HttpContext和Unit测试方面设计BaseController的更好方法是什么。

1 个答案:

答案 0 :(得分:6)

  

单元测试HttpContext的正确方法是什么?

绝对没办法。当此上下文仍未初始化时,您正在控制器的构造函数内使用HttpContext。不仅无法测试此代码,而且当您运行应用程序时,它也会因NRE而崩溃。你永远不应该在控制器的构造函数中使用任何与HttpContext相关的东西。

一种可能性是重构代码并在Initialize方法中执行此操作:

public class BaseController : Controller
{
    private IPolicy Policy;

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

这就是说,这不是我推荐的方法。我建议您使用依赖注入而不是服务位置,这被许多人视为反模式。

所以:

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

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

现在,剩下的就是配置你喜欢的Dependency Injection框架,将正确的实例注入到构造函数中。例如,使用Ninject.Mvc3,只需一行代码即可实现:

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

现在你可以在单元测试中自由地模仿这个IPolicy而不用关心任何HttpContext。

例如,假设您有以下要进行单元测试的控制器:

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

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

现在,您需要做的就是选择您喜欢的模拟框架(在我的情况下是Rhino Mocks)并进行模拟:

[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());
}