0

I have property defined like this:

[Required(ErrorMessage="The Year of Manufacture has to be filled")]
public int? YearOfManufacture { get; set; }

How do I write Unit test, which tests, if the property has set the required Annotation?

Braiam
  • 1
  • 11
  • 47
  • 78
Pavel Straka
  • 427
  • 7
  • 19

3 Answers3

2

You could have a test helper method that will run the validation on a particular instance of your view model:

public bool TryValidate(object model, out ICollection<ValidationResult> results)
{
    var context = new ValidationContext(model, serviceProvider: null, items: null);
    results = new List<ValidationResult>();
    return Validator.TryValidateObject(
        model, context, results, validateAllProperties: true
    );
}

and then in your unit test:

[TestMethod]
public void YearOfManufacture_Is_Required()
{
    // arrange
    var sut = new MyViewModel();
    sut.YearOfManufacture = null;
    ICollection<ValidationResult> results;

    // act
    var actual = TryValidate(sut, out results);

    // assert
    Assert.IsFalse(actual);
}

Alternatively to using DataAnnotations you may take a look at FluentValidation.NET which allows you to express much more complex validation rules in a fluent manner, it has a nice integration with MVC and testing your validation rules is even easier.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
2

I'd recommend FluentAssertions for this, which allows you to do this very cleanly:

typeof(MyType)
    .GetProperty("YearOfManufacture")
    .Should()
    .BeDecoratedWith<RequiredAttribute>("because MyType.YearOfManufacture is required.");
Todd Menier
  • 37,557
  • 17
  • 150
  • 173
  • I don't like this test at all: the property name is weakly typed as a string and it tests an explicit implementation (attribute decoration). What if the validation is done through a different system, like fluent validation rules? No change in functionality yet all the tests break. – Jeroen Vannevel Apr 11 '15 at 16:15
  • Fair points and definitely worth mentioning. One *might* invoke YAGNI on future-proofing it against switching validation frameworks, but it depends on the larger context. I went for a succinct answer to the question asked. – Todd Menier Apr 11 '15 at 17:42
1

If you - for what ever reason - want to test, that a property is annotated (instead of testing that a validation annotation works), you can use the extension method suggested here in your test:

public static T GetAttributeFrom<T>(this object instance, string propertyName) where T : Attribute
{
    var attrType = typeof(T);
    var property = instance.GetType().GetProperty(propertyName);
    return (T)property .GetCustomAttributes(attrType, false).FirstOrDefault();
}

EDITED: (1) used FirstOrDefault() instead of First() to correctly handle non-annotated attributes. (2) @Pavel Straka: Added more sample code below to answer Pavel Straka's comment.

class Sample
{
    [Required(ErrorMessage = "The Year of Manufacture has to be filled")]
    public int? YearOfManufacture { get; set; }

    public int? NotAttributedProperty { get; set; }
}

static class Annotations
{
    public static T GetAttributeFrom<T>(this object instance, string propertyName) where T : Attribute
    {
        var attrType = typeof(T);
        var property = instance.GetType().GetProperty(propertyName);
        return (T)property.GetCustomAttributes(attrType, false).FirstOrDefault();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var sampleInstance = new Sample();
        var annotation = sampleInstance.GetAttributeFrom<RequiredAttribute>("YearOfManufacture");
        var nullAnnotation = sampleInstance.GetAttributeFrom<RequiredAttribute>("NotAttributedProperty");
    }
}
Community
  • 1
  • 1
JimiLoe
  • 950
  • 2
  • 14
  • 22
  • I get error: System.Array does not contain a definition for First and no extension method First accepting a first argument of Type System.Array could be found (are you missing a using directive or assembly reference)? I can't add reference to System.Array. How could I solve this? Thank you – Pavel Straka Apr 11 '15 at 15:53