Model state validation in unit tests

23,630

Solution 1

The reason the model state is valid is that a new model state is created when you new up a controller. Web API isn't doing the parameter binding for you here, so it doesn't even have a chance to add model state errors.

If you want to keep this as a unit test, then you should add the model state errors yourself and test what happens.

If you want to test that the model state would be invalid on a real request, I recommend you read this blog post:

http://blogs.msdn.com/b/youssefm/archive/2013/01/28/writing-tests-for-an-asp-net-webapi-service.aspx

and try testing against an in-memory server. One minor note for your case would be that you may want to use a StringContent instead of an ObjectContent on the request to make sure that Web API tries to deserialize and bind the body properly.

Solution 2

TL;DR If you don't want to read the entire article provided by Youssef and want a quick solution to how to make ModelState.IsValid return false. Do this.

[TestMethod]
public void TestLogin_InvalidModel()
{
    AccountController controller = CreateAccountController();
    // new code added -->
    controller.ModelState.AddModelError("fakeError", "fakeError");
    // end of new code
    ...
    var response = controller.PostLogin(new LoginModel() {  });

    Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);

}

Now I can imagine the CreateAccountController() looks something like this for minimum ->

return new AccountApiController()
{
    Request = new HttpRequestMessage(),
    Configuration = new HttpConfiguration()
};

Hope this gives a quick answer for those googling :)

Solution 3

As mentioned before, you need integration tests to validate the ModelState. So, with Asp.Net Core, I'm digging this question to add a simple solution for integrating tests with Asp.Net Core and validation of ModelState

Add the package Microsoft.AspNetCore.TestHost and you can submit requests this simple:

var server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
var client = server.CreateClient();
var model = new { Name = String.Empty };
var content = new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json");
var result = await client.PostAsync("/api/yourApiEndpoint", content);
result.StatusCode.Should().Be(HttpStatusCode.BadRequest);

You can find more about it here: http://asp.net-hacker.rocks/2017/09/27/testing-aspnetcore.html

Hope it helps.

Solution 4

I used the following to validate the model state in unit test Visual studio 2017, C#, NET 4.x.x

   [TestMethod]
        public void TestYourValidationModel()
        {
            var configuration = new HttpConfiguration();
            configuration.Filters.Add(new ValidateModelAttribute());
            // Get the quote
            var controller = new YourController
            {
                Request = new HttpRequestMessage(),
                Configuration = configuration
            };
            var request = YourRequestObject;
            controller.Request.Content = new ObjectContent<YourRequestType>(
                request, new JsonMediaTypeFormatter(), "application/json");
            controller.Validate(request);
            Assert.IsTrue(controller.ModelState.IsValid, "This must be valid");
        }

The example is for a request in JSON format. Substitute YourController for the name of your controller, and YourRequesType, for the object type of your request.

This give you the option to test your model for validation without go to the service.

Share:
23,630
bandreas
Author by

bandreas

Software Developer I write code and eventually read it.

Updated on July 09, 2022

Comments

  • bandreas
    bandreas almost 2 years

    I am writing a unit test for a controller like this:

    public HttpResponseMessage PostLogin(LoginModel model)
    {
        if (!ModelState.IsValid)
            return new HttpResponseMessage(HttpStatusCode.BadRequest);
    }
    

    the model looks like:

    public class LoginModel
    {
        [Required]
        public string Username { set; get; }
        [Required]
        public string Password { set; get; }
    }
    

    Then I have unit test like this one:

    [TestMethod]
    public void TestLogin_InvalidModel()
    {
        AccountController controller = CreateAccountController();
    
        ...
        var response = controller.PostLogin(new LoginModel() {  });
    
        Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode);
    
    }
    

    Actually the ModelState is validated... which is weird for me as both fields are required... Could anybody advise?

  • Kiran
    Kiran almost 11 years
    +1 with Youssef. Also, you can take a look at the following Web API poster for the request pipeline. asp.net/posters/web-api/ASP.NET-Web-API-Poster-grayscale.pdf
  • cah1r
    cah1r over 7 years
    Thanks for the example. The only difference I made was type of assert: Assert.IsInstanceOfType(actResult, typeof(InvalidModelStateResult));
  • THBBFT
    THBBFT over 6 years
    The right answer if you're looking for a .net core answer.
  • 845614720
    845614720 over 2 years
    Note that you can simply use await client.PostAsJsonAsync("api/v1/endpoint", object); if you prefer less code.