3

I am starting to learn Unit testing in MVC4.

This is my controller.

public class AccountController : Controller
{
    public ActionResult Register(User user)
    {
        if (ModelState.IsValid)
        {
            return View("RegistrationSuccessful");                
        }
        return View("Register");
    }
}

And this is the test.

public class AccountControllerTests
{
    [TestMethod]
    public void invalid_registration_details_should_show_registration_form_again()
    {
        var controller = new AccountController();
        var user = new User();
        user.Name = null;
        var result = controller.Register(user) as ViewResult;
        Assert.AreEqual("Register", result.ViewName);
    }
}

And this is the model.

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

When I call controller.Register(user) I think the model binder does not come in to picture as I am instantiating the controller myself and not through the framework. So I think ModelState.IsValid will be true by default.

How do I test this? How can model validation be triggered in unit tests?

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
Cracker
  • 1,780
  • 4
  • 24
  • 34

4 Answers4

2

You can also achieve by below approach to validate model. In my case below ProjectViewModel is my model. In below scenario, I didn't set Description property to validate if validation working.

[TestMethod]
[ExpectedException(typeof(ValidationException), "Please enter description.")]
public void Create_Project_Empty_Description()
{
    ProjectViewModel model = new ProjectViewModel
    {
        ProjectID = 3,
        Name = "Test Project",
        StartDate = DateTime.Now.Date,
        EndDate = DateTime.Now.AddMonths(-1),
        States = new List<ProjectStateViewModel> { new ProjectStateViewModel { StateName = "Pending" } }
    };

    ValidationContext contex = new ValidationContext(model);
    Validator.ValidateObject(model, contex);
}
Nps
  • 1,638
  • 4
  • 20
  • 40
  • Here's another variation on this. http://stackoverflow.com/questions/2167811/unit-testing-asp-net-dataannotations-validation – pwDev Feb 23 '15 at 16:40
2

Since there is no logic in the controller other than checking if ModelState.IsValid==true, I think you might just want a unit test for your model.

First, your model. I would change it to look something like this:

public class User : IValidatableObject
{
    public string Name { get; set; }
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        // I personally add all my validation here instead of adding [Required] to specific properties, this helps me know all of my validation is in this one place and add other complex validations if needed
        // This code here determines if ModelState.IsValid 
        var retVal = new List<ValidationResult>();
        if (string.IsNullOrEmpty(this.Name))
        {
            retVal.Add(new ValidationResult("Name is Empty", new List<string> { "Name" }));
        }
        return retVal;
    }
}

Second, the unit test. I would write tests where I would expect a specific error message for a specific property. In this example I am giving a null value and expecting the error message "Name is Empty" for the property "Name":

    [TestMethod]
    public void NullOrEmptyName()
    {
        var model = new YourProject.Models.Account.User { Name= null };
        var validationContext = new ValidationContext(model);
        var validation = model.Validate(validationContext).ToList();
        Assert.AreEqual(validation.Count(x => x.ErrorMessage == "Name is empty" && x.MemberNames.Single() == "Name"), 1);
    }
Jacob Heck
  • 53
  • 1
  • 7
0

I'm not sure if it is possible to trigger model validation but I know how to test it.

Try this code:

    [TestClass]
    public class AccountControllerTests
    {
        [TestMethod]
        public void invalid_registration_details_should_show_registration_form_again()
        {
            // arrange
            var controller = new AccountController();
            controller.ViewData.ModelState.AddModelError("Key", "ErrorMessage");

            // act
            var result = controller.Register(new User()) as ViewResult;

            // assert
            Assert.AreEqual("Register", result.ViewName);
        }

        [TestMethod]
        public void valid_registration_details_should_show_registration_successful_page()
        {
            // arrange
            var controller = new AccountController();

            // act
            var result = controller.Register(new User()) as ViewResult;

            // assert
            Assert.AreEqual("RegistrationSuccessful", result.ViewName);
        }
    }
Ilya Palkin
  • 14,687
  • 2
  • 23
  • 36
0

You can do it by forcing it to validate your model by calling following from test. For my tests, i created a TestHelper class inside which mocked this method and added ModelState.AddModelError() for specific conditions.

TryValidateModel(model);// will do the task

May be you need to mock the ControllerContext as well for this.

Rajesh Mishra
  • 436
  • 5
  • 15