How to register a common factory object?

I have the following two classes:

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, }; } } 

The reason the internal constructor and the second class exist, I want to apply more KeyedEntity.Create(x, y) syntax rather than new KeyedEntity<T>{ Key = x, Entity = y } . (Note that the type is inferred using the previous syntax.)

I want to tell AutoFixture how to create an instance of KeyedEntity . However, the Register method, apparently, allows only one type to be registered, not a public generic type.

How to register KeyedEntity.Create<TEntity> as a create function for KeyedEntity<TEntity> ?

+3
source share
2 answers

To maintain your open generic type, you can write your own sample constructor:

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

(Defensive coding is omitted for clarity.)

The following unit test passes:

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

For best maintainability, you should encapsulate KeyedEntityBuilder in the setup .

+3
source

Assuming you have a set of derived types, for example:

 public class A: KeyedEntity<A> { } public class B: KeyedEntity<B> { } 

Since the above graph of objects contains a circular link (to T ), you need to configure the Fixture instance to exclude assignments during the first recursion:

Then you Create a generic method that will set up the creation algorithm for KeyedEntity<T> :

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

You can use the above method like:

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

Example

 [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>(); } 
0
source

All Articles