dev

Unit Testing of Custom Validation Property Attributes

Testing of the ValidationProperties is quite simple. You just call yourAttribute.IsValid method with instance of some test class. But we found that if you annotate you custom attribute with

[AttributeUsage(AttributeTargets.Property)]

The IsValid method is not executed. Ok, there are another possibilities how to test validation attributes. We can use System.ComponentModel.DataAnnotations.Validator class and its methods TryValidateObject (for classes) and TryValidateProperty (for properties). For this purpose I have created helper class for testing these attributes for both object(class) and property.

    public class ValidationAttributeTestHelper
    {
        public static IList<ValidationResult> GetValidationErrorsForObject(object model)
        {
            var validationContext = new ValidationContext(model, null, null);
            var validationResults = new List<ValidationResult>();
            Validator.TryValidateObject(model, validationContext, validationResults);
            return validationResults;
        }

        public static void CheckValidationErrorsForObject(object model, IEnumerable<string>; expectedValidationMessages)
        {
            var errors = GetValidationErrorsForObject(model);
            errors.Count.ShouldBeEquivalentTo(expectedValidationMessages.Count());
            var i = 0;
            foreach (var msg in expectedValidationMessages)
            {
                msg.ShouldBeEquivalentTo(errors[i++].ErrorMessage);
            }
        }

        public static IList<ValidationResult> GetValidationErrorsForProperty(object model, object property, string propertyName)
        {
            var validationContext = new ValidationContext(model, null, null) { MemberName = propertyName };
            var validationResults = new List<ValidationResult>();
            Validator.TryValidateProperty(property, validationContext, validationResults);
            return validationResults;
        }

        public static void CheckValidationErrorsForProperty(object model, object property, string propertyName, IEnumerable<string> expectedValidationMessages)
        {
            var errors = GetValidationErrorsForProperty(model, property, propertyName);
            errors.Count.ShouldBeEquivalentTo(expectedValidationMessages.Count());
            var i = 0;
            foreach (var msg in expectedValidationMessages)
            {
                msg.ShouldBeEquivalentTo(errors[i++].ErrorMessage);
            }
        }
    }

The usage of this helper class is following:

  1. Create a test class which will be annotated with attributes to be tested
  2. Use the test helper class
public class TestClass
{
     public string Password { get; set; }
     [NotEqualTo("Password")] //tested attribute
     public string PasswordCheck { get; set; }
}

[TestMethod]
public void Test_NotEqualToAttribute_With_Set_Same_Passwords_Should_Return_Invalid_Result()
{
     //Arrange
     var testee = new TestClass { Password = "samePassword", PasswordCheck = "samePassword" };

     //Act

     //Assert
     ValidationAttributeTestHelper.CheckValidationErrorsForProperty(testee, testee.PasswordCheck, "PasswordCheck", new[] { "PasswordCheck cannot be the same as Password" });
}

Thats all.

Advertisement
dev

Entity Framework, DbContext, IDbContextFactory and CodeFirst Migration Problem

In one of our project, we had to establish logging of the Db queries. In EF it is quite easy – just set your logger into DBContext.Database.Log action.

public MyContext(ILogger logger)
:this()
{
     Logger = logger;
     Database.Log = message => Logger.Info(this, message);
}

Here comes first issue. Basically, the context is initialized automatically by EF, you just define its type in configuration classes (e.g. DbMigrationsConfiguration). But it works only if you have defined MyContext with parameterless constructor. But in our case we have MyContext with constructor which accept ILogger parameter because we want to inject it by IoC container. For this purpose you have to create factory class which inherits from IDbContextFactory interface.

    public class DatabaseContextFactory : IDbContextFactory<MyContext>
    {
        public MyContext Create()
        {
            return IocFactory.GetContainer().Resolve<MyContext>();
        }
    }

Nice, now EF finds this factory and uses it for MyContext initialization. So, we don’t need the parameterless constructor, do we? Let’s delete this unused code! Let’s run the app – it is working, nice! Let’s run CodeFirst migration from the Package Manager Console – wait, it fails! We don’t, until we try to run CodeFirts migration from the Package Manager Console. In our case, the console throws following exception:

Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
System.Runtime.Serialization.SerializationException: Type is not resolved for member
'Microsoft.Practices.Unity.ResolutionFailedException+ResolutionFailedExceptionSerializationData,
Microsoft.Practices.Unity, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.

It seems there is referenced another library version or what?! Let’s debug it to found the problem with resolving or IoC configuration. But it is not possible to debug it when you run the PowerShell command. After the investigation, the problem is in used IoC container – it’s base initialization is run in Web app. Ok, so we separated part of type registration to be able to use it in DataAccess project too. Exception changed to another one. Next we tried to put parameterless constructor of MyContext back. Tried to run migration command and it run correctly! Next, we tried to debug application and checked EF initialization – IDbContextFactory was used to initialize MyContext with ILogger parameter.

So, the suggestion is – even you are using IDbContextFactory to initialize you context, do not remove the parameterless constructor.

Note: I‘m not sure if it is the best or correct solution, so in case of better one, leave comment.