使用具有Moq的相同泛型类型的特定子类动态填充模拟对象的特定泛型属性

时间:2018-01-19 16:05:37

标签: c# generics reflection moq

我正在尝试创建一个可以构建Mock通用接口的泛型类,以帮助我完成各种单元测试功能。

我需要构建的模拟对象使其DbSet<T>个实例填充TestDbSet<T>个实例(TestDbSet<T>继承自DbSet<T>)。

对于一个具体的例子,给定接口:

public interface IAppointmentSchedulerContext
{
    DbSet<ServiceCenter> ServiceCenters { get; set; }
}

我希望能够让我的TestDbContextBuilder类构造一个模拟实例,其中ServiceCenters属性填充了ServiceCenter<T>的实例。

这是我到目前为止所做的:

public class TestDbContextBuilder<TDbContextInterface> where TDbContextInterface : class
{
    public TDbContextInterface Build()
    {
        var mock = new Mock<TDbContextInterface>();
        var dbSetProps = typeof(TDbContextInterface).GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                                    .Where(pi => pi.PropertyType.IsGenericType &&
                                                                 pi.PropertyType.GetGenericTypeDefinition() ==
                                                                 typeof(DbSet<>));

        //populate all the DbSet<TEntity> properties (in dbSetProps) on the mock with TestDbSet<TEntity> instances

        return mock.Object;
    }
}

如果我静态地执行此操作,在通用构建器之外,我会这样做:

mock.SetupProperty(m=>m.ServiceCenters).Returns(new TestDbSet<ServiceCenter>());

但是对于通用DbSet<>接口上的所有TDbContextInterface属性,这是如何动态的?

1 个答案:

答案 0 :(得分:0)

我终于能够让这个工作了。最终的代码是:

public class TestDbContextBuilder<TDbContextInterface> where TDbContextInterface : class
{
    public TDbContextInterface Build()
    {
        var mock = new Mock<TDbContextInterface>();
        mock.SetupAllProperties();

        var dbSetProps = typeof(TDbContextInterface).GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                                    .Where(pi => pi.PropertyType.IsGenericType &&
                                                                 pi.PropertyType.GetGenericTypeDefinition() ==
                                                                 typeof(DbSet<>));
        foreach (var prop in dbSetProps)
        {
            var dbSetType = prop.PropertyType;
            var dbSetGenericType = dbSetType.GetGenericArguments()[0];

            var testDbSetType = typeof(TestDbSet<>).GetGenericTypeDefinition().MakeGenericType(dbSetGenericType);
            var testDbSetInstance = Activator.CreateInstance(testDbSetType);

            prop.SetValue(mock.Object, testDbSetInstance);
            }

        return mock.Object;
    }
}

我被prop.SetValue(mock.Object, testDbSetInstance);离开财产null的时间最长。关键是在尝试设置值之前添加对mock.SetupAllProperties()的调用。

我还没有超级对此代码感到满意,因为调用Mock.Object会“锁定”模拟,以便不再对其进行更改。我有修复它的想法,但它将涉及在表达树中深入挖掘。