0

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?

Community
  • 1
  • 1
Jeroen
  • 60,696
  • 40
  • 206
  • 339

1 Answers1

1

I suggest to use this helper function to find all types that are somehow based on ICollection or ICollection<>:

private static bool IsOrImplementsICollection(Type t)
{
    if (t == typeof (ICollection) || (t.IsGenericType && t.GetGenericTypeDefinition() == typeof (ICollection<>)))
        return true;
    return t.GetInterfaces().Any(IsOrImplementsICollection);
}

In your test then you can change the where clause to:

var collectionProps = type
    .GetProperties()
    .Where(x => IsOrImplementsICollection(x.PropertyType));
thehennyy
  • 4,020
  • 1
  • 22
  • 31