Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing a Custom Validation Filter

I have the following attribute:

public class ValidateModelAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (actionContext.ModelState.IsValid == false)
            {
                actionContext.Response = actionContext.Request.CreateErrorResponse(
                    HttpStatusCode.BadRequest, actionContext.ModelState);
            }
        }
    }  

I have this generic extension method for Determining whether the attribute is applied to a method or not

public static bool ActionHasFilter(this ApiController controller, string action, Type filter)
    {
        var controllerType = controller.GetType();
        var method = controllerType.GetMethod(action);

        object[] filters = method.GetCustomAttributes(filter, true);

        return filters.Any(x=>x.GetType() == filter);

    }

My problem is how do i go about testing whether the attribute actually works or not, without testing the controller action?

Let's say i have the following Entity

public class UserViewModel
{
     [Required]
     public string Name {get; set;}
     [Required]
     [EmailAddress]
     public string Email {get;set;
}

How would i go about mocking the context and checking whether the model is valid?

I'm using Nunit and Moq.

like image 346
MrBliz Avatar asked Jan 10 '23 09:01

MrBliz


1 Answers

Spock's solution is on the right track, but is a bit too invasive on the code, because it makes the ValidateModelAttribute class dependent on TestableHttpActionContext. My implementation uses a public property that will be used to "inject" the Request object for the Unit Tests, while the implementation as attribute continues to use the Request object from the ActionContext:

public class ValidateModelAttribute : ActionFilterAttribute
{
    public HttpRequestMessage TestRequestMessage { get; set; }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        PerformValidation(actionContext, TestRequestMessage ?? actionContext.Request);
    }

    private void PerformValidation(HttpActionContext actionContext, HttpRequestMessage request)
    {
        if (actionContext.ModelState.IsValid == false)
        {
            actionContext.Response = request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
        }
    }
}

Unit tests:

[Test]
public void OnActionExecuting_ValidModel_ResponseIsNotSet()
{
    var actionContext = new HttpActionContext();

    actionContext.ModelState.Clear();

    var attribute = new ValidateModelAttribute { TestRequestMessage = new HttpRequestMessage() };

    attribute.OnActionExecuting(actionContext);

    Assert.IsNull(actionContext.Response);
}

[Test]
public void OnActionExecuting_InvalidModel_ResponseIsSetToBadRequest()
{
    var actionContext = new HttpActionContext();

    actionContext.ModelState.AddModelError("key", "error");

    var attribute = new ValidateModelAttribute() { TestRequestMessage = new HttpRequestMessage() };

    attribute.OnActionExecuting(actionContext);

    Assert.AreEqual(HttpStatusCode.BadRequest, actionContext.Response.StatusCode);
}

Please note that I'm not using an actual model to validate the ModelState, because that's out of scope for the Unit Tests: We want to test the ModelState's outcome, not the ModelState itself. ;-)

like image 160
Prutswonder Avatar answered Mar 28 '23 02:03

Prutswonder