Как я могу зарегистрировать фабрику универсальных объектов?
У меня есть следующие два класса:
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>();
}