【问题标题】:Initialize BaseController's fields using HttpContext, controller design使用HttpContext初始化BaseController的字段,控制器设计
【发布时间】:2012-05-26 00:21:22
【问题描述】:

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

  public class BaseController : Controller
    {

        private IPolicy Policy;

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

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

   this.httpContextBase.User.

问题:(更新)

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

【问题讨论】:

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


【解决方案1】:

单元测试 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;
    }
}

现在,剩下的就是配置您最喜欢的依赖注入框架,将正确的实例注入到构造函数中。例如,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());
}

【讨论】:

  • 在 Initialize 方法中,HttpContext 不为空,但在单元测试时不会调用该方法(实例化 BaseController 的子类)。因此,访问 Policy 字段时出现“未将引用设置为对象”的异常
  • @Pingpong,抱歉我的回答不完整。我忘了提供正确的设计方法。
  • 在依赖注入时,Policy 构造函数需要 HttpContextBase 参数,IoC 容器是否能够注入它。我正在使用 Unity IoC?感谢您的建议!
  • 我不了解 Unity,但 Ninject 会自动执行此操作。
  • 通过重构BaseController,HttpContext不是NULL吗?这个想法是 IPolicy 应该仅通过 BaseController 更改,但现在每个控制器都可能分配不同类型的 IPolicy。
猜你喜欢
  • 2013-07-04
  • 1970-01-01
  • 1970-01-01
  • 2018-09-04
  • 1970-01-01
  • 2015-04-16
  • 1970-01-01
  • 2012-01-18
  • 2011-01-06
相关资源
最近更新 更多