1

I have a custom attribute named: requiredIf. This attribute is related to a custom DataAnnotationsModelValidator, named RequiredIfValidator.

I declare this mapping in file Global.asax.cs :

DataAnnotationsModelValidatorProvider.RegisterAdapter( typeof( RequiredIfAttribute ), typeof( RequiredIfValidator ) );

On runtime it works fine :)

But I want to test via a unit test the validation of my model. Guess than we have the following

public class MyModel
{
    public bool A {get;set;}

    [RequiredIf ("A", true)]
    public bool? B {get;set;} 
}

This model is valid when A is false, and when A is true B need to set to true or false.

I try to test this validation with the following Unit Test:

var viewModel = new MyModel();
var context = new ValidationContext( viewModel, null, null );
var results = new List<ValidationResult>();
viewModel.A = false;
var isModelStateValid = Validator.TryValidateObject( viewModel, context, results, true );

Assert.IsFalse( isModelStateValid );

And the assertion failed. It's logic because my RequiredIfValidator is not setted. So my question is how can I set this custom validator to run my Unit Test?

Thanks,

  • I solve my issue by using a custom model validation implementation. To implement this method I use the way describe here : https://magedfarag.wordpress.com/2012/10/17/unit-testing-mvc-controllers-with-model-validation/ – user2210680 Aug 12 '15 at 07:14

1 Answers1

0

I managed to get this working with the following extension method:

public static class ViewModelExtensions
{
    public static IReadOnlyCollection<ValidationResult> GetValidationResults<T>(this T viewModel, Func<Type, object> serviceProvider = null)
    {
        var result = GetComponentValidationResults(viewModel, serviceProvider);

        var modelValidationResults = GetModelValidationResults(viewModel);

        return result
            .Concat(modelValidationResults.ToValidationResults())
            .ToList();
    }

    private static List<ValidationResult> GetComponentValidationResults<T>(T viewModel, Func<Type, object> serviceProvider)
    {
        var vc = new ValidationContext(viewModel);
        if (serviceProvider != null)
        {
            vc.InitializeServiceProvider(serviceProvider);
        }

        var result = new List<ValidationResult>();
        Validator.TryValidateObject(viewModel, vc, result, true);
        return result;
    }

    private static IEnumerable<ModelValidationResult> GetModelValidationResults<T>(T model)
    {
        var modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, typeof(T));

        /* build a stub controller context */
        var controllerContext = Fake.ControllerContext.Build();

        var validator = ModelValidator.GetModelValidator(modelMetadata, controllerContext);
        return validator.Validate(null);
    }

    private static IEnumerable<ValidationResult> ToValidationResults(this IEnumerable<ModelValidationResult> results)
    {
        return results.Select(r => r.ToValidationResult());
    }

    private static ValidationResult ToValidationResult(this ModelValidationResult result)
    {
        return new ValidationResult(result.Message, new[] {result.MemberName});
    }
}

This can be used like:

// arrange
var target = new MyViewModel();

// act
var actual = target.GetValidationResults(StubServiceProvider);

// assert
/* Assertions for whatever the expected validation failure might be */

Note:

  • the Fake.ControllerContext.Build() call is a factory that produces a stub controller context based on something like this: https://stackoverflow.com/a/32672/90609
  • I managed to get this working for my scenario where I had registered a custom adapter for the RequiredAttribute.
RikRak
  • 898
  • 1
  • 7
  • 21