2

I am trying to run a simple test on a controller in MVC. This controller validates the input using xVal and DataAnnotations. When I run the test (using NUnit via Resharper, NUnit standalone or TestDriven.Net) it crashes the runner with no decent error message. In the event logs it just has a fairly generic .NET Runtime 2.0 Error Reporting message saying that the runner was a Faulting application.

The error is caused by a call to ModelState.IsValid (I know this because when I take it out it runs fine). Also, the crash only occurs when I run the test normally. When I run the test in debug mode it works fine.

When I remove the references to xVal and set an error on the modelstate using ModelState.AddModelError, it doesn't crash.

Below is the controller under test and the test class. Can you see anything amiss here?

Controller Under Test

using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;
using xVal.ServerSide;

namespace TestModelState.Controllers
{
    public class ThingController : Controller
    {
        [HttpPost]
        public ActionResult Create(Thing thing)
        {
            try
            {
                var errors = DataAnnotationsValidationRunner.GetErrors(thing);
                if (errors.Any())
                {
                    throw new RulesException(errors);
                }
            }
            catch (RulesException ex)
            {
                ex.AddModelStateErrors(ModelState, "thing");
            }
            if (ModelState.IsValid)
            {
                // Do some save stuff here
                return RedirectToAction("Index");
            }
            else
            {
                return View();
            }
        }
    }

    public class Thing
    {
        [Required]
        public string Name { get; set; }
    }

    internal static class DataAnnotationsValidationRunner
    {
        public static IEnumerable<ErrorInfo> GetErrors(object instance)
        {
            return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>()
                   from attribute in prop.Attributes.OfType<ValidationAttribute>()
                   where !attribute.IsValid(prop.GetValue(instance))
                   select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
        }
    }
}

Test Class

using System.Web.Mvc;
using NUnit.Framework;
using TestModelState.Controllers;

namespace TestModelState.Tests
{
    [TestFixture]
    public class ThingControllerTests
    {
        [Test]
        public void Create_InvalidThing_SetsModelState()
        {
            // Arrange
            var thingController = new ThingController();
            var thing = new Thing();

            // Act
            var result = thingController.Create(thing);

            // Assert
            var viewResult = (ViewResult)result;
            Assert.IsFalse(viewResult.ViewData.ModelState.IsValid);
        }
    }
}

Versions - ASP.Net MVC - 2.0.0.0, NUnit - 2.5.3.9345, xVal - 1.0.0.0

Update When I use the following statement instead (which is what ModelState.IsValid is doing under the covers) the crash does not occur...

var modelStateIsValid = ModelState.Values.All(ms => ms.Errors.Count == 0);

I'd still prefer to use ModelState.IsValid, but at least this is a workaround.

Russell Giddings
  • 8,731
  • 5
  • 34
  • 35

1 Answers1

0

The ModelState.IsValid is not set by the controller, it is set by the model binding framework. Model binding is framework is fired only when processing the incoming HTTP requests. When you call the controller actions explicitly like above, the model binding does not happen and hence whole ModelState is in an indeterminate state. you have got few ways

  1. Mimic the whole model binding stuff in your tests as given in this SO question - How can I test ModelState?

  2. You can use TryUpdateModel method in your controller action as given in this SO question - Unit tests on MVC validation

  3. Write separate unit tests that test for the attribute based validations for your models. For this you would need to change your approach towards attributes based validation. Read this article for details - http://jesschadwick.blogspot.com/2009/04/cleaner-validation-with-aspnet-mvc.html

Community
  • 1
  • 1
Suhas
  • 7,919
  • 5
  • 34
  • 54