ASP.NET Core 2.1自定义ActionFilterAttribute单元测试descriptor为null修复求助
Ah, I spot the issue immediately! Your test is using a generic ActionDescriptor instance, but your filter expects a ControllerActionDescriptor—that's why the cast returns null, and none of your validation logic actually runs. Let's fix this step by step.
The Root Cause
Your filter depends on ControllerActionDescriptor to access the action method's parameters via MethodInfo.GetParameters(). But in your original test, you're passing a base ActionDescriptor (via Mock.Of<ActionDescriptor>()), which can't be cast to the more specific ControllerActionDescriptor type.
Fixed Test Code
Here's the adjusted test that properly sets up a valid ControllerActionDescriptor and ensures your filter can execute its full logic:
[TestMethod] public void ValidateModelAttributes_SetsResultToBadRequest_IfModelIsInvalid() { // Arrange var validationFilter = new ValidateModelStateAttribute(); var modelState = new ModelStateDictionary(); modelState.AddModelError("name", "invalid"); // Create a dummy controller method to get valid MethodInfo for the descriptor // This mimics a real controller action with a parameter named "name" var dummyActionMethod = typeof(DummyTestController).GetMethod(nameof(DummyTestController.ValidateNameAction)); // Use ControllerActionDescriptor instead of the base ActionDescriptor var actionDescriptor = new ControllerActionDescriptor { MethodInfo = dummyActionMethod, ControllerName = nameof(DummyTestController), ActionName = nameof(DummyTestController.ValidateNameAction) }; var actionContext = new ActionContext( Mock.Of<HttpContext>(), Mock.Of<RouteData>(), actionDescriptor, // Pass the valid ControllerActionDescriptor here modelState ); // Match the ActionArguments key to the parameter name from our dummy method var actionExecutingContext = new ActionExecutingContext( actionContext, new List<IFilterMetadata>(), new Dictionary<string, object>() { { "name", "test-value" } }, // Key matches parameter name Mock.Of<Controller>() ); // Act validationFilter.OnActionExecuting(actionExecutingContext); // Assert Assert.IsNotNull(actionExecutingContext.Result); // Optional: Add a more specific assertion to confirm it's a BadRequest Assert.IsInstanceOfType(actionExecutingContext.Result, typeof(BadRequestObjectResult)); } // Dummy controller class to provide a valid MethodInfo for the test private class DummyTestController : Controller { public IActionResult ValidateNameAction(string name) { return Ok(); } }
Key Changes Explained
- Dummy Controller & Action: We added a lightweight
DummyTestControllerwith an action method that has anameparameter. This gives us a validMethodInfoto assign to theControllerActionDescriptor, so your filter can callGetParameters()without null reference errors. - Concrete ControllerActionDescriptor: Instead of using a mocked base
ActionDescriptor, we create a realControllerActionDescriptorinstance and populate its required properties (MethodInfo,ControllerName,ActionName). - Matching Action Arguments: The
ActionArgumentsdictionary now uses a key (name) that exactly matches the parameter name from our dummy action. This ensures your filter can retrieve the argument value correctly during validation.
With these changes, the cast context.ActionDescriptor as ControllerActionDescriptor will return a valid instance, your filter will run through the parameter validation logic, and your assertion will pass when the model state is invalid.
内容的提问来源于stack exchange,提问作者Kevin YANG

