Создать экземпляры Moq для универсальных типов - PullRequest
0 голосов
/ 30 апреля 2018

У меня проблема с настройкой макета (используя moq).

У нас есть общий репозиторий:

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

    [...]
}

И я хотел бы динамически создавать репозитории, которые будут возвращены нашим 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;
}

Для этого я реализовал следующие методы установки имитации:

    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
    {
        [...]
    }

Модель данных: конкретные реализации расширяют абстрактные базисные данные, которые расширяют абстрактные EntityBase

Теперь моя проблема в том, что переданный тип GetQueryableMockDbSet всегда будет возвращать экземпляр Mock<DbSet<Basisdata>> вместо DbSet конкретной реализации в строке var repositoryInstance = Activator.CreateInstance(constructedRepository, GetQueryableMockDbSet(new[] { basisdataInstance }).Object);, что, очевидно, приводит к исключению, поскольку T не соответствует для хранилища. и DBSet.

Вопрос: Как заставить GetQueryableMockDbSet возвращать набор DB для правильного типа?

Обратите внимание, что я хочу, чтобы это было динамически, и я не знаю всех сущностей, расширяющих Базисданные.

[Edit] здесь методы тестирования и настройки:

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

1 Ответ

0 голосов
/ 30 апреля 2018

Проблема в том, что вы используете универсальные методы с неявно выведенными типами. Кроме Basisdata[] все другие типы использования var и обобщенные значения с <T>, которые все компилятор разрешает в Basisdata. Поскольку Moq использует тот же механизм для определения типа, вместо того, чтобы смотреть на тип переданного объекта, вы получите DbSet<Basisdata>.

Вы можете обойти это с помощью универсального класса mock-builder, который вы также создаете с помощью отражения. Я быстро собрал это и проверил, скажите, работает ли он и у вас:

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());

Редактировать: Сокращенный код до одного статического класса, который может создавать Mocks.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...