我在设置模拟(使用最小起订量)时遇到问题。
我们有一个通用的存储库:
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)
这里的问题是您使用具有隐式推断类型的泛型方法。除了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 的单个静态类。