为泛型类型创建 Moq 实例

Phi*_*ppe 3 c# generics moq

我在设置模拟(使用最小起订量)时遇到问题。

我们有一个通用的存储库:

public class Repository<T> : IRepository<T>
    where T : EntityBase
{
    public Repository(DbSet<T> set)
    {
        Set = set;
    }

    [...]
}
Run Code Online (Sandbox Code Playgroud)

我想动态创建将由我们的 IRepositoryResolver 返回的存储库:

public interface IRepositoryResolver
{
    TRepository Resolve<TRepository, TEntity>()
        where TRepository : IRepository<TEntity>
        where TEntity : EntityBase;

    IRepository<TEntity> ResolveFor<TEntity>()
        where TEntity : EntityBase;

    IRepository<TEntity> ResolveFor<TEntity>(TEntity entity)
        where TEntity : EntityBase;
}
Run Code Online (Sandbox Code Playgroud)

为此,我实现了这些模拟设置方法:

    private void SetupRepositoryResolverMock()
    {
        var basisDataTypes = Assembly.GetAssembly(typeof(Basisdata))
            .GetTypes()
            .Where(p => p.IsClass && !p.IsAbstract
                                  && typeof(Basisdata).IsAssignableFrom(p))
            .ToList();

        Basisdata[] basisdataInstances = new Basisdata[basisDataTypes.Count];
        for (int i = 0; i < basisDataTypes.Count; i++)
        {
            basisdataInstances[i] = (Basisdata)Activator.CreateInstance(basisDataTypes.ElementAt(i));
        }

        _repositoryResolverMock = new Mock<IRepositoryResolver>();
        foreach (var basisdataInstance in basisdataInstances)
        {
            Type genericRepository = typeof(Repository<>);
            Type constructedRepository = genericRepository.MakeGenericType(basisdataInstance.GetType());
            var repositoryInstance = Activator.CreateInstance(constructedRepository, GetQueryableMockDbSet(new[] { basisdataInstance }).Object);
            //_repositoryResolverMock
            //    .Setup(x => x.ResolveFor(basisdataInstance))
            //    .Returns(() => repositoryInstance);
        }
    }

    private static Mock<DbSet<T>> GetQueryableMockDbSet<T>(ICollection<T> sourceList) where T : EntityBase
    {
        [...]
    }
Run Code Online (Sandbox Code Playgroud)

数据模型是:具体实现扩展抽象Basisdata,扩展抽象EntityBase

现在我的问题是,通过 GetQueryableMockDbSet 传递的类型将始终返回一个实例Mock<DbSet<Basisdata>>而不是一个具体实现的 DbSet,var repositoryInstance = Activator.CreateInstance(constructedRepository, GetQueryableMockDbSet(new[] { basisdataInstance }).Object);这显然会导致异常,T因为与存储库和 DBSet 不匹配。

问题:如何让 GetQueryableMockDbSet 返回正确类型的 DBset?

请注意,我希望这是动态的,并且不知道扩展 Basisdata 的所有实体。

[编辑] 这里的测试和设置方法:

    [SetUp]
    public void Setup()
    {
        SetupServiceMock();
        SetupRepositoryResolverMock();
        SetupPersonalModuleMock();

        _onlineSyncServicePersonal = new OnlineSyncServicePersonal(_serviceMock.Object, _repositoryResolverMock.Object, new[] { _personalModuleMock.Object });
    }

    [Test]
    public void CheckoutTest()
    {
        // arrange
        var checkoutRequest = new CheckoutRequest
        {
            DienstId = Guid.NewGuid(),
            OrganisationId = Guid.NewGuid(),
            CheckoutId = Guid.NewGuid(),
            LockModuleNames = new[]
            {
                Constants.ModuleName.BASE,
                Constants.ModuleName.PERSONAL
            }
        };

        // act
        var checkoutResult = _onlineSyncServicePersonal.Checkout(checkoutRequest);

        // assert
    }
Run Code Online (Sandbox Code Playgroud)

Tox*_*ron 6

这里的问题是您使用具有隐式推断类型的泛型方法。除了Basisdata[]所有其他类型用法是var<T>编译器都解析为的泛型Basisdata。由于Moq使用相同的机制来定义类型,而不是查看传递对象的类型,您最终会得到DbSet<Basisdata>.

您可以使用通用的模拟构建器类来解决这个问题,您也可以使用反射创建该类。我快速构建并测试了它,告诉我它是否也适合你:

public class MockCreator
{
    public static Mock CreateMock(Type genericType, Type itemType)
    {
        var typeToMock = genericType.MakeGenericType(itemType);
        var creator = typeof(Mock<>).MakeGenericType(typeToMock);
        return (Mock)Activator.CreateInstance(creator);
    }
}


// Usage
var types = new Type[0]; // Your entity types
var sets = types.Select(t => MockCreator.CreateMock(typeof(DbSet<>), t)).ToList();

// Or in your case 
var setMock = MockCreator.CreateMock(typeof(DbSet<>), basisdataInstance.GetType());
Run Code Online (Sandbox Code Playgroud)

编辑:将代码简化为可以创建 Mock 的单个静态类。