63

I have a custom asp.net mvc class validation attribute. My question is how can I unit test it? It would be one thing to test that the class has the attribute but this would not actually test that the logic inside it. This is what I want to test.

[Serializable]
[EligabilityStudentDebtsAttribute(ErrorMessage = "You must answer yes or no to all questions")]
public class Eligability
{
    [BooleanRequiredToBeTrue(ErrorMessage = "You must agree to the statements listed")]
    public bool StatementAgree { get; set; }

    [Required(ErrorMessage = "Please choose an option")]
    public bool? Income { get; set; }

.....removed for brevity }

[AttributeUsage(AttributeTargets.Class)]
public class EligabilityStudentDebtsAttribute : ValidationAttribute
{
    // If AnyDebts is true then 
    // StudentDebts must be true or false

    public override bool IsValid(object value)
    {
        Eligability elig = (Eligability)value;
        bool ok = true;
        if (elig.AnyDebts == true)
        {
            if (elig.StudentDebts == null)
            {
                ok = false;
            }
        }
        return ok;

    }
}

I have tried to write a test as follows but this does not work:

[TestMethod]
public void Eligability_model_StudentDebts_is_required_if_AnyDebts_is_true()
{

   // Arrange
   var eligability = new Eligability();
   var controller = new ApplicationController();

   // Act
   controller.ModelState.Clear();
   controller.ValidateModel(eligability);
   var actionResult = controller.Section2(eligability,null,string.Empty);

   // Assert
   Assert.IsInstanceOfType(actionResult, typeof(ViewResult));
   Assert.AreEqual(string.Empty, ((ViewResult)actionResult).ViewName);
   Assert.AreEqual(eligability, ((ViewResult)actionResult).ViewData.Model);
   Assert.IsFalse(((ViewResult)actionResult).ViewData.ModelState.IsValid);
}

The ModelStateDictionary does not contain the key for this custom attribute. It only contains the attributes for the standard validation attributes.

Why is this?

What is the best way to test these custom attributes?

3 Answers 3

74

Your attribute EligabilityStudentDebtsAttribute is just a standard class, like everything else, just unit test the IsValid() method. If it works OK, trust to Framework that attribute works OK.

So:

[Test]
public void AttibuteTest()
{
   // arrange
   var value = //.. value to test - new Eligability() ;
   var attrib = new EligabilityStudentDebtsAttribute();

   // act
   var result = attrib.IsValid(value);

   // assert
   Assert.That(result, Is.True)
}
Sign up to request clarification or add additional context in comments.

5 Comments

Is this the best way to do cross property validation? (Model validation that involves more than one property)
keep it simple.. try, if works to you - no reason to make more compex testing :)
What if my attribute requires the state like other properties?
Remember to add a reference to System.ComponentModel.DataAnnotations in your unit test assembly or else you don't see the IsValid method (it also won't compile but it took me a while to spot this)
There is a way to test simple types directly without the need for an object with attribute. I have an answer below. It allows additionally to get the message from the attribute directly.
40

Your custom validation attribute might be dependent on the state of other properties. In this case you can use the System.ComponentModel.DataAnnotations.Validator static methods, for example:

var model = ...
var context = new ValidationContext(model);
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(model, context, results, true);
Assert.True(isValid);

1 Comment

you might want to add the true flag to validate all properties - "Validator.TryValidateObject(model, context, results, true);" - I had problems when testing my validation with NUnit, the validation inside "protected override ValidationResult IsValid(..)" did not get hit unless i provided the "validateAllProperties" as true - hence the tests did not run as expected and I could not debug into my code.
13

I have found out that IsValid does not work well on simple types like string. E.g. if you have a validation on a string query parameter that is not an object. Additionally it's easier to test a value directly on the attribute without having to provide a whole object. It also allows to check the error message. This is how it works:

string input = "myteststring";
var myAttribute = new MyAttribute()
var result = attribute.GetValidationResult(input, new ValidationContext(input));

var isSuccess = result == ValidationResult.Success;
var errorMessage = result?.ErrorMessage;

This code tests only the validation of your input value and nothing else.

P.S. I have tested this in dotnet core, but I would think this works for ordinary dotnet as well.

1 Comment

From my perspective, this is the best answer. This allows a person to test their validation type in isolation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.