I'm looking to test an ActionFilterAttribute in a .NET Core 2.0 API project and wondering the best way to go about it. Note, I'm not trying to test this through a controller action, merely test the ActionFilterAttribute itself.
How might I go about testing this:
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
}
To demonstate the different features of action filters, different custom ActionFilters have been defined. Each filter writes to the output window. public class Action1DebugActionWebApiFilter : ActionFilterAttribute
You can use an action filter, for instance, to modify the view data that a controller action returns. Result filters contain logic that is executed before and after a view result is executed. For example, you might want to modify a view result right before the view is rendered to the browser.
Filters for Web API are not the same as filters for MVC. The Web API filters are found in the System.Web.Http.Filters namespace. To demonstate the different features of action filters, different custom ActionFilters have been defined.
Only groups of filters and in this example, all filters from group IActionFilter are overrided. This example is the same as example 1, except the action method has 4 different ActionFilters defined. As shown in the results, the order of the ActionFilters on the action method is NOT sorted!
Create an instance of the context pass it to the filter and assert the expected behavior
For example
[TestClass]
public class ValidateModelAttributeTest {
[TestMethod]
public void Invalid_ModelState_Should_Return_BadRequestObjectResult() {
//Arrange
var modelState = new ModelStateDictionary();
modelState.AddModelError("", "error");
var httpContext = new DefaultHttpContext();
var context = new ActionExecutingContext(
new ActionContext(
httpContext: httpContext,
routeData: new RouteData(),
actionDescriptor: new ActionDescriptor(),
modelState: modelState
),
new List<IFilterMetadata>(),
new Dictionary<string, object>(),
new Mock<Controller>().Object);
var sut = new ValidateModelAttribute();
//Act
sut.OnActionExecuting(context);
//Assert
context.Result.Should().NotBeNull()
.And.BeOfType<BadRequestObjectResult>();
}
}
Here is a real life example, where I also access the method info and params inside the action filter attribute:
Suppose I have a Controller Method with ActionAttribute like this:
public class HomeController : Controller
{
...
[FeatureFlagActionAtrribute("user", new String[] { "Feature1" })]
public IActionResult DoSomethingWithFilterAction(String user)
{...}
}
The http call would be something like this:
/Home/DoSomethingWithFilterAction?user="user1"
Now, I want to test the ActionAttribute FeatureFlagActionAtrribute in such a context.
If you would apply above suggestion to this example, it would look like this (worked for me, at least)
var methodInfoDoSomethingWithFilterAction = typeof(HomeController).GetMethod(nameof(HomeController.DoSomethingWithFilterAction));
var httpContext = new DefaultHttpContext();
var routeData = new RouteData();
FeatureFlagActionAtrribute FeatureFlagActionAtrributeFilter = methodInfoDoSomethingWithFilterAction.GetCustomAttribute<FeatureFlagActionAtrribute>();
ActionDescriptor actionDescriptor = new ControllerActionDescriptor()
{
ActionName = methodInfoDoSomethingWithFilterAction.Name,
ControllerName = typeof(FeatureFlagTest).Name,
DisplayName = methodInfoDoSomethingWithFilterAction.Name,
MethodInfo = methodInfoDoSomethingWithFilterAction,
};
ActionContext actionContext = new ActionContext(httpContext, routeData, actionDescriptor) ;
var homeController = new HomeController();
var attribute = new FeatureFlagActionAtrribute("user", new string[] { "feature1" });
IDictionary<string, object> actionArguments = new Dictionary<string, object>
{
["user"] = "user1"
};
var filterMetadata = new List<IFilterMetadata>() { featureFlagActionAtrributeFilter };
ActionExecutingContext actionExecutedContext = new
ActionExecutingContext(actionContext, filterMetadata, actionArguments, homeController);
attribute.OnActionExecuting(actionExecutedContext);
Then inside the ActionFilterAttribute:
public override void OnActionExecuting(ActionExecutingContext context)
{
ControllerActionDescriptor actionDescriptor = (ControllerActionDescriptor)context.ActionDescriptor;
Debug.Print($"2. @Before Method called {actionDescriptor.ControllerName}Controller.{actionDescriptor.ActionName}");
var controllerName = actionDescriptor.ControllerName;
var actionName = actionDescriptor.ActionName;
IDictionary<object, object> properties = actionDescriptor.Properties;
ParameterInfo[] paramsOfMethod = actionDescriptor.MethodInfo.GetParameters();
var fullName = actionDescriptor.DisplayName;
var paramNameForKeyOfFeature = ParamNameForKeyOfFeature;
var arguments = context.ActionArguments;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With