繁体   English   中英

为什么我的asp.net mvc控制器在单元测试期间没有调用它的基类'Initialize方法?

[英]Why does my asp.net mvc controller not call it's base class' Initialize method during unit tests?

我正在尝试对我的控制器进行单元测试,并且我使用默认的MVC 3 AccountController单元测试作为基础。 到目前为止,我有我的控制器,看起来像:

public partial class HomeController : MyBaseController
{
    public HomeController(IUnitOfWork unitOfWork) { _unitOfWork = unitOfWork; }

    public virtual ActionResult Index()
    {

        return View();
    }

    public virtual ActionResult About()
    {
        return View();
    }
}

MyBaseController具有以下代码:

public class MyBaseController : Controller
{
    public MyJobLeadsBaseController()
    {
        CurrentUserId = 0;
    }

    protected override void Initialize(RequestContext requestContext)
    {
        if (MembershipService == null) { MembershipService = new AccountMembershipService(); }
        base.Initialize(requestContext);

        // If the user is logged in, retrieve their login id
        var user = MembershipService.GetUser();
        if (user != null)
            CurrentUserId = (int)user.ProviderUserKey;
    }

    public int CurrentUserId { get; set; }
    public IMembershipService MembershipService { get; set; }

    protected IUnitOfWork _unitOfWork;
}

当我运行实际站点时,一切正常,断点显示正确触发了Initialize() 但是,以下单元测试从不运行Initialize(RequestContext)方法:

    [TestMethod]
    public void Index_Shows_When_Not_Logged_In()
    {
        // Setup
        HomeController controller = new HomeController(_unitOfWork);
        controller.MembershipService = new MockMembershipService(null);
        SetupController(controller);

        // Act
        ActionResult result = controller.Index();

        // Verify
        Assert.IsNotNull(result, "Index returned a null action result");
        Assert.IsInstanceOfType(result, typeof(ViewResult), "Index did not return a view result");
    }

    protected static void SetupController(Controller controller)
    {
        RequestContext requestContext = new RequestContext(new MockHttpContext(), new RouteData());
        controller.Url = new UrlHelper(requestContext);

        controller.ControllerContext = new ControllerContext
        {
            Controller = controller,
            RequestContext = requestContext
        };
    }

通过此单元测试进行调试表明,在任何时候都不会调用重写的MyBaseController.Initialize() 这会导致我的CurrentUserId属性未在单元测试中设置但在实时系统上设置的问题。

还有什么办法可以触发Initialize()来调用?

当您通过网站向控制器发出请求时,MVC框架会接收该请求并通过许多不同的步骤运行它。 在那个步骤流程中的某个地方,MVC知道它必须调用Initialize方法,以便在MyBaseController类中找到Initialize并执行它。 在这一点上一切都很顺利,一切正常。

在测试代​​码中创建HomeController的新实例时,您只需创建一个类的新实例。 您没有通过MVC请求管道运行该类,并且您的测试框架不知道执行Initialize方法因此您收到错误。

就个人而言,我希望独立于Initialize方法测试Index操作。 您上面的Index操作是空的,所以我无法确切地看到您正在尝试做什么,但如果您为登录用户返回一个视图而另一个为匿名用户返回,我会执行以下操作:

[TestMethod]
public void Index_Shows_When_Not_Logged_In(){
    HomeController controller = new HomeController(_unitOfWork);
    controller.CurrentUserId=0;

    controller.Index();

    //Check your view rendered in here
}

[TestMethod]
public void Some_Other_View_Shows_When_Logged_In(){
    HomeController controller = new HomeController(_unitOfWork);
    controller.CurrentUserId=12; //Could be any value

    controller.Index();

    //Check your view rendered in here
}

MVC contrib项目( http://mvccontrib.codeplex.com/ )中有一些非常漂亮的测试帮助程序,它们允许您执行以下操作:

controller.Index().AssertViewRendered();

构造函数不会调用Initialize。 IIRC调用initialize的是Execute / ExecuteCore方法。

以下是MVC源代码:

protected virtual void Execute(RequestContext requestContext) {
            if (requestContext == null) {
                throw new ArgumentNullException("requestContext");
            }
            if (requestContext.HttpContext == null) {
                throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
            }

            VerifyExecuteCalledOnce();
            Initialize(requestContext);

            using (ScopeStorage.CreateTransientScope()) {
                ExecuteCore();
            }
        }

这基本上是从BeginProcessRequest方法中的MvcHandler调用的。

更新:

单元测试中不存在RequestContext,因为您没有通过ASP.NET。 如果我没记错的话,你需要嘲笑它。

在测试中使用Initialize的另一个问题是成员资格提供程序,我自己没有使用过,但我猜想AccountMembershipService会在测试中失败吗? 在我看来,这会产生脆弱的测试。 如果它必须联系服务器,它可能也会减慢你的速度,并且可能会在CI服务器上失败。

IMO,从基本看看Init方法,它不应该存在。 想到的最简单的解决方案是使用依赖注入来在Controller ctor中注入CurrentUserId。

在你“运行实际站点”的情况下,我怀疑它调用了HomeController的默认构造函数,而HomeController又会调用基本控制器的correpsonding ctor。

尝试修改公共HomeController(IUnitOfWork unitOfWork)的自定义ctor

public HomeController(IUnitOfWork unitOfWork):base()

这将确保在调用时调用基本控制器的默认ctor。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM