dimanche 20 mars 2016

Using reflection to test that all collection properties of classes will be instantiated

I'm trying to craft a unit test that will check for a given assembly that all non-abstract classes with empty constructors will instantiate their collection properties when instantiated. Here's the system under test:

namespace MyProject.Dto.Things
{
    public class Item
    {
        public Item()
        {
            // Second batch of props should be instantiated here...
        }

        // Properties that my test doesn't/shouldn't care about:
        public int IntProp { get; set; }
        public string StringProp { get; set; }

        // Properties my test should account for:
        public List<string> ListProp { get; set; }
        public IList<string> IListProp { get; set; }
        public ISet<string> ISetProp { get; set; }
        public ICollection<string> ICollectionProp { get; set; }
        public IDictionary<string, string> IDictionaryProp { get; set; }
        public Stack<string> StackProp { get; set; }
        public string[] ArrayProp { get; set; }
    }
}

My first attempt whas this:

[TestFixture]
public class DtoTests
{
    [Test]
    public void NamespaceDtos_WhenDefaultConstructorIsCalled_InstantiatesCollectionProperties()
    {
        bool testWasMeaningful = false;

        foreach (var type in GetEntityTypesWithDefaultConstructor())
        {
            var instance = Activator.CreateInstance(type);
            var collectionProps = type
                .GetProperties()
                .Where(p => typeof(ICollection<>).IsAssignableFrom(p.PropertyType));

            foreach (var prop in collectionProps)
            {
                var val = prop.GetValue(instance);
                Assert.That(val, Is.Not.Null.And.Empty, string.Format("[{0}.{1}]", type.Name, prop.Name));
                testWasMeaningful = true;
            }
        }

        Assert.That(testWasMeaningful, "Expected at least one assertion.");
    }

    private IEnumerable<Type> GetEntityTypesWithDefaultConstructor()
    {
        return Assembly
            .GetAssembly(typeof(MyProject.Dto.Things.Item))
            .GetTypes()
            .Where(t => !t.IsAbstract)
            .Where(t => t.GetConstructor(Type.EmptyTypes) != null);
    }
}

However, this does not work, because the Where clause does not grab the correct properties from my other assembly. My test fails because it tests not a single property.


Attempted fix no 1

I've tried this answer like this:

var collectionProps = type
    .GetProperties()
    .Where(m => m.PropertyType.IsGenericType && m.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>));

But that only catches Item.ICollectionProp, and fails to test all others.


Attempted fix no 2

Second, I've tried this answer to another question (using the GetICollectionOrICollectionOfTProperties method ad verbatim), like this:

var collectionProps = type
    .GetICollectionOrICollectionOfTProperties();

But ironically, that will incorrectly pass the test even if Item.ICollectionProp is not instantiated.


Attempted fix no 3

I've also tried testing just all IEnumerable properties, like this:

var collectionProps = type
    .GetProperties()
    .Where(p => typeof(IEnumerable).IsAssignableFrom(p.PropertyType));

But that will fail on Item.StringProp, because string is IEnumerable, and I do not want to test for that here.


I'm not sure where I go wrong, especially with nr 2. I'd even think my question is a duplicate of that one, if I'd only had gotten the solution to work.

Bottom line: (X) how can I test that all collection properties of classes with empty constructors are instantiated when the class is instantiated, and/or (Y) how can I find all collection properties of a type using reflection?





Aucun commentaire:

Enregistrer un commentaire