【问题标题】:How do I unit test my asp.net-mvc controller's OnActionExecuting method?如何对我的 asp.net-mvc 控制器的 OnActionExecuting 方法进行单元测试?
【发布时间】:2013-12-31 03:30:20
【问题描述】:

我已经重写了我的控制器的 OnActionExecuting 方法,以根据正在执行的 filterContext 设置一些内部状态。我该如何测试这个?该方法本身是受保护的,所以我认为我必须在调用堆栈中更高。

我需要什么代码来测试这个?

我正在使用 mvc RC 1。

编辑:我也在使用 nunit。

谢谢

【问题讨论】:

    标签: asp.net-mvc unit-testing


    【解决方案1】:

    您需要添加和使用私有访问器。右键单击您的控制器类并从菜单中选择Create Private Accessors 并将它们添加到您的测试项目中。进入测试项目后,创建控制器,然后为其创建访问器。该方法应该在访问器上可用。这是我自己的代码中的示例测试:

    /// <summary>
    ///A test for OnActionExecuting
    ///</summary>
    [TestMethod()]
    [ExpectedException( typeof( InvalidOperationException ) )]
    public void OnActionExecutingWindowsIdentityTest()
    {
        var identity = WindowsIdentity.GetCurrent();
        WindowsPrincipal principal = new WindowsPrincipal( identity );
        var httpContext = MockRepository.GenerateStub<HttpContextBase>();
        httpContext.User = principal;
    
        var actionDescriptor = MockRepository.GenerateStub<ActionDescriptor>();
    
        RouteData routeData = new RouteData();
    
        BaseController controller = new BaseController();
        BaseController_Accessor accessor = new BaseController_Accessor( new PrivateObject( controller ) );
        ControllerContext controllerContext = MockRepository.GenerateStub<ControllerContext>( httpContext, routeData, controller );
    
        ActionExecutingContext filterContext = new ActionExecutingContext( controllerContext, actionDescriptor, new Dictionary<string, object>() );
    
        accessor.OnActionExecuting( filterContext );
    
    }
    

    编辑:如果您没有使用 MSTest 进行单元测试,则可能必须手动生成访问器。本质上,您创建一个包装器类,通过等效的公共方法公开被测类的私有/受保护方法,将被测类的实例传递给包装器,然后使用包装器类的反射来调用私有/受保护的方法被测类的方法。

       public class MyClass
       {
           protected void DoSomething( int num )
           {
           }
       }
    
       public class MyClass_accessor
       {
           private MyClass privateObj;
    
           public MyClass_accessor( MyClass obj )
           {
               this.privateObj = obj;
           }
    
           public void DoSomething( int num )
           {
               MethodInfo info = privateObj.GetType()
                                           .GetMethod("DoSomething",
                                                       BindingFlags.NonPublic
                                                       | BindingFlags.Instance );
    
               info.Invoke(obj,new object[] { num });
           }
        }
    

    【讨论】:

    • 我只是问,因为我没有看到“添加访问器”上下文按钮,并且进行谷歌搜索会将我带到一些特定于 ms 测试的东西。
    • 是的,我想是的。我想它至少需要 VS Pro 才能存在。您可以手动创建一个,并使用反射在底层私有对象上调用适当的方法。
    • 通过 MSTest 添加的访问器并没有什么神奇之处,它们只是使用反射。它们很好,因为您不必生成所有代码。我在回答中举了一个例子。请注意,您也可以通过反射直接在测试中调用该方法,但它可能违反 DRY。
    • 感谢您的帮助。我有 VS 专业版。我能够通过右键单击并转到“创建单元测试...”并选择我想要测试的私有方法来生成访问器。这创建了一个 MSTest 项目,并生成了相应的访问器。
    • 而不是每次都经历这些步骤,尽管我会像你描述的那样手工制作访问器。谢谢你的例子。
    【解决方案2】:

    我最近遇到了类似的问题,找不到令人满意的解决方案。所以我创建了我自己的辅助函数来调用 OnActionExecuted 和 OnActionExecuting。在此处查看代码http://mkramar.blogspot.com.au/2012/06/onactionexecuting-and-onactionexecuted.html

    【讨论】:

    • 注意接受的答案如何包括代码和解释,并且缺少任何自我宣传链接?这是可接受答案的模式。
    • 我没有让另一个工作。但是您的代码有效。也许我应该编辑您的答案并添加您的代码。我将您的代码与stackoverflow.com/a/10316899/648076 结合在一起。现在测试看起来很不错。
    【解决方案3】:

    我试图这样做,但实际上我想测试自定义属性的结果,因为它应用于实际控制器。在我们的例子中,我们有一个在控制器上设置属性的授权属性,然后控制器使用这些属性。我们的代码如下所示:

    // Create the controller to test
    PortalController controller = new PortalController();
    var method = typeof(PortalController);
    var attribute = method.GetCustomAttributes(typeof(OrganizationContextFilter),true).Cast<OrganizationContextFilter>().SingleOrDefault();
    
    // Set the controller Context with our fake objects on it
    controller.ControllerContext = this.GetDefaultControllerMock(controller);
    
    // Execute the Organization Context Filter
    var actionDescriptor = new Mock<ActionDescriptor>();
    var context = Mock.Get(actionDescriptor.Object);
    context.Setup(s => s.ControllerDescriptor).Returns(new Mock<ControllerDescriptor>().Object);
    
    // Use the current controller Context
    ActionExecutingContext filterContext = new ActionExecutingContext( controller.ControllerContext, actionDescriptor.Object, new Dictionary<string, object>() );
    attribute.OnActionExecuting(filterContext);
    
    // We have to use this one, because it has the result of the Attribute execution
    PortalController pc = filterContext.Controller as PortalController;
    ActionResult result = pc.MethodToTest(); // Call the controller that had OnActionExecuting results
    

    这样做的好处是我们实际上在我们实际测试的控制器上执行了自定义 MVC 属性。这既练习了自定义属性代码,又在更像“真实世界”的情况下测试了控制器。

    【讨论】:

    • 在事实发生 2 年后提出一个简单的问题 - 如何通过派生属性设置控制器属性?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多