How do I unit test my asp.net-mvc controller's OnActionExecuting method?

前端 未结 3 1152
不思量自难忘°
不思量自难忘° 2021-02-08 10:49

I\'ve overridden my controller\'s OnActionExecuting method to set some internal state based on the executing filterContext. How do I test this? The method itself is protected so

相关标签:
3条回答
  • 2021-02-08 11:11

    I was trying to do this, but I actually wanted to test the outcome of the custom attribute as it applied to the actual controller. In our case we had an authorization attribute that set properties on the controller, the controller then used the properties. Our code looks something like this:

    // 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
    

    The benefit of this is that we actually execute the custom MVC attribute on the controller we are actually testing. This both exercises the custom attribute code and tests the controller in a situation that is more like the "real world".

    0 讨论(0)
  • 2021-02-08 11:15

    You need to add and use a Private Accessor. Right click in your controller class and choose Create Private Accessors from the menu and add them to your test project. Once in your test project, create your controller, then create an accessor for it. The method should be available on the accessor. Here's a sample test from my own code:

    /// <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 );
    
    }
    

    EDIT: If you aren't using MSTest for your unit tests, you may have to generate the accessors by hand. Essentially, you make a wrapper class that exposes the private/protected methods of the class under test via equivalent public methods, pass an instance of the class under test to the wrapper, and then use reflection from the wrapper class to invoke the private/protected method on the class under test.

       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 });
           }
        }
    
    0 讨论(0)
  • 2021-02-08 11:19

    I recently had a similar problem and could not find satisfying solution. So I created my own helper function that invokes OnActionExecuted and OnActionExecuting. See code here http://mkramar.blogspot.com.au/2012/06/onactionexecuting-and-onactionexecuted.html

    0 讨论(0)
提交回复
热议问题