Как я могу зарегистрировать фабрику универсальных объектов?

У меня есть следующие два класса:

public class KeyedEntity<TEntity>
{
    internal KeyedEntity() { }

    public Identifier Key { get; set; }
    public TEntity Entity { get; set; }
}

public static class KeyedEntity
{
    public static KeyedEntity<TEntity> Create<TEntity>(Identifier key, TEntity entity)
    {
        return new KeyedEntity<TEntity>
        {
            Key = key,
            Entity = entity,
        };
    }
}

Причина, по которой конструктор internal и второй класс существует, я хочу, чтобы обеспечить более высокий уровень обслуживания KeyedEntity.Create(x, y) синтаксис, а не new KeyedEntity<T>{ Key = x, Entity = y }, (Обратите внимание, что тип выводится с прежним синтаксисом.)

Я хочу рассказать AutoFixture, как создать экземпляр KeyedEntity, Тем не менее Register Кажется, что метод позволяет регистрировать только один тип, а не открытый универсальный тип.

Как я могу зарегистрироваться KeyedEntity.Create<TEntity> в качестве функции создания для KeyedEntity<TEntity>?

2 ответа

Решение

Чтобы поддержать ваш открытый универсальный тип, вы можете написать собственный конструктор образцов:

public class KeyedEntityBuilder : ISpecimenBuilder
{
    private readonly static MethodInfo createMethod =
        typeof(KeyedEntity).GetMethod("Create");

    public object Create(object request, ISpecimenContext context)
    {
        var t = request as Type;
        if (t == null ||
            !t.IsGenericType ||
            t.GetGenericTypeDefinition() != typeof(KeyedEntity<>))
            return new NoSpecimen(request);

        var entityType = t.GetGenericArguments().Single();

        var key = context.Resolve(typeof(Identifier));
        var entity = context.Resolve(entityType);

        return createMethod
            .MakeGenericMethod(entityType)
            .Invoke(null, new[] { key, entity });
    }
}

(Защитное кодирование опущено для ясности.)

Проходит следующий юнит тест:

public class Tests
{
    [Fact]
    public void CreateKeyedEntity()
    {
        var fixture = new Fixture();
        fixture.ResidueCollectors.Add(new KeyedEntityBuilder());

        var actual = fixture.Create<KeyedEntity<Foo>>();

        Assert.NotNull(actual.Key);
        Assert.NotNull(actual.Entity);
    }
}

Для лучшей ремонтопригодности, вы должны заключать в капсулу KeyedEntityBuilder в настройке.

Предполагая, что у вас есть набор производных типов, например:

public class A: KeyedEntity<A>
{
}

public class B: KeyedEntity<B>
{
}

Поскольку приведенный выше граф объектов содержит круговую ссылку (на T) вам нужно настроить Fixture Например, чтобы пропустить присваивания при первой рекурсии:

Затем вы Создайте общий метод, который настроит алгоритм создания для KeyedEntity<T>:

internal void CustomizeKeyedEntity<T>(IFixture fixture)
{
    fixture.Customize<KeyedEntity<T>>(c =>
        c.FromFactory(() =>
            KeyedEntity.Create(
                fixture.Create<Identifier>(),
                fixture.Create<T>())));
}

Вы можете использовать вышеуказанный метод как:

this.CustomizeKeyedEntity<A>(fixture);
this.CustomizeKeyedEntity<B>(fixture);

пример

[Fact]
public void Test()
{
    var fixture = new Fixture();

    this.CustomizeKeyedEntity<A>(fixture);
    this.CustomizeKeyedEntity<B>(fixture);

    var actualA = fixture.Create<A>();
    var actualB = fixture.Create<B>();
}
Другие вопросы по тегам