使用反射来测试将实例化类的所有集合属性

时间:2016-03-20 13:11:51

标签: c# unit-testing generics reflection

我正在尝试制作一个单元测试,它将检查给定的程序集,所有带有空构造函数的非抽象类在实例化时都会实例化它们的集合属性。这是被测系统:

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; }
    }
}

我的第一次尝试是这样的:

[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);
    }
}

但是,这不起作用,因为Where子句没有从我的其他程序集中获取正确的属性。我的测试失败了,因为它测试的不是一个属性。

尝试修复1

我已尝试this answer这样:

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

但这只会抓住Item.ICollectionProp,而无法测试所有其他人。

尝试修复2

其次,我尝试了this answer to another question(逐字使用GetICollectionOrICollectionOfTProperties方法),如下所示:

var collectionProps = type
    .GetICollectionOrICollectionOfTProperties();

但具有讽刺意味的是,即使Item.ICollectionProp未实例化,也会错误地通过测试。

尝试修复3

我也尝试过测试所有IEnumerable属性,例如:

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

但是Item.StringProp会失败,因为stringIEnumerable,我不想在这里测试。

我不确定我哪里出错了,尤其是2号。我甚至认为我的问题与那个问题重复,如果我只是得到了解决方案。

底线:(X)如何在实例化类时测试具有空构造函数的类的所有集合属性,和/或(Y)如何使用反射找到类型的所有集合属性?< / p>

1 个答案:

答案 0 :(得分:1)

我建议使用此辅助函数查找基于ICollectionICollection<>的所有类型:

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

在测试中,您可以将where子句更改为:

var collectionProps = type
    .GetProperties()
    .Where(x => IsOrImplementsICollection(x.PropertyType));