Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing Mvc.Compare Attribute incorrectly returns model isValid = true

TryValidateObject does not seem to work on with the Compare model validation attribute when unit testing.

I get ModelState.IsValid = true, when I know it is false (when unit testing).

I've got this example model:

public class CompareTestModel
{
    public string Password { get; set; }

    [System.Web.Mvc.Compare(
         "Password",
          ErrorMessage = "The passwords do not match")]
    public string PasswordCompare { get; set; }
}

Using this helper method to validate models when unit testing:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

public static class ModelHelper
{
    public static void ValidateModel(
         this Controller controller,
         object viewModel)
    {
        controller.ModelState.Clear();

        var validationContext = new ValidationContext(viewModel, null, null);
        var validationResults = new List<ValidationResult>();

        Validator.TryValidateObject(
            viewModel,
            validationContext,
            validationResults,
            true);

        foreach (var result in validationResults)
        {
            foreach (var name in result.MemberNames)
            {
                controller.ModelState.AddModelError(name, result.ErrorMessage);
            }
        }
    }
}

And I run this unit test:

    [Test]
    public void CompareAttributeTest()
    {
        // arrange
        var model = new CompareTestModel();
        model.Password = "password";
        model.PasswordCompare = "different password";

        AccountController controller = new AccountController();

        // act
        controller.ValidateModel(model);

        // assert
        Assert.IsFalse(controller.ModelState.IsValid);
    }
like image 244
Robs Avatar asked Nov 02 '22 13:11

Robs


1 Answers

The CompareAttribute does not fill in the ValidationResult's class MemberNames property (see source). So your result.MemberNames will be empty.

Because it is not required to use the MemberNames property (the ValidationResult even has a constructor for this) so you need to change your ValidateModel helper to deal with this kind of ValidationResult:

foreach (var result in validationResults)
{
    if (result.MemberNames.Any())
    {
        foreach (var name in result.MemberNames)
        {
            controller.ModelState.AddModelError(name, result.ErrorMessage);
        }
    }
    else
        controller.ModelState.AddModelError("", result.ErrorMessage);
}
like image 103
nemesv Avatar answered Nov 15 '22 05:11

nemesv