AutoFixture: Customizing Open Generics Sample Designer

I have an object model that uses Open Generics (yes, yes, now I have two problems: that’s why I am here :): -

public interface IOGF<T> { } class C { } class D { readonly IOGF<C> _ogf; public D( IOGF<C> ogf ) { _ogf = ogf; } } 

I am trying to get AutoFixture to create anonymous D instances above. However, AutoFixture itself does not have a built-in strategy for building IOGF<> and, therefore, we observe:

 public class OpenGenericsBinderDemo { [Fact] public void X() { var fixture = new Fixture(); Assert.Throws<Ploeh.AutoFixture.ObjectCreationException>( () => fixture.CreateAnonymous<D>() ); } 

Main message:

Ploeh.AutoFixture.ObjectCreationException: AutoFixture could not create an instance from IOGF`1 [C], most likely because it does not have an open constructor, is abstract or non-public.

I am pleased to provide it with a specific implementation:

 public class OGF<T> : IOGF<T> { public OGF( IX x ) { } } public interface IX { } public class X : IX { } 

And related binding:

 fixture.Register<IX,X>(); 

How can I (or should I even look at the problem this way?) To make the next test pass?

 public class OpenGenericsLearning { [Fact] public void OpenGenericsDontGetResolved() { var fixture = new Fixture(); fixture.Inject<IX>( fixture.Freeze<X>() ); // TODO register or do something that will provide // OGF<C> to fulfill D IOGF<C> requirement Assert.NotNull( fixture.CreateAnonymous<D>()); } } 

(There are discussions and problems on the Codeplex website - I just needed to quickly figure it out, and I'm open to removing this if it's just a bad idea and / or I missed something)

EDIT 2: (See also comment on Mark's answer.) The context (admittedly, far-fetched) is an acceptance test on a large "almost complete" system chart of the "System under test" object, and not on a small (controlled / easily accessible): pairs or triplets of classes in a single or integration test scenario. As mentioned in the summary statement about myself, I'm not entirely sure that this type of test even makes sense.

+7
source share
2 answers

You can create a setting that works as follows:

 public class AnOpenGenericsBinderDemo { [Fact] public void RegisteringAGenericBinderShouldEnableResolution() { var fixture = new Fixture(); fixture.Inject<IX>( fixture.Freeze<X>() ); fixture.RegisterOpenGenericImplementation( typeof( IOGF<> ), typeof( OGF<> ) ); Assert.IsType<OGF<C>>( fixture.CreateAnonymous<D>().Ogf ); } } 

And it runs like this:

 public static class AutoFixtureOpenGenericsExtensions { public static void RegisterOpenGenericImplementation( this IFixture that, Type serviceType, Type componentType ) { if ( !serviceType.ContainsGenericParameters ) throw new ArgumentException( "must be open generic", "serviceType" ); if ( !componentType.ContainsGenericParameters ) throw new ArgumentException( "must be open generic", "componentType" ); // TODO verify number of type parameters is 1 in each case that.Customize( new OpenGenericsBinderCustomization( serviceType, componentType ) ); } public class OpenGenericsBinderCustomization : ICustomization { readonly Type _serviceType; readonly Type _componentType; public OpenGenericsBinderCustomization( Type serviceType, Type componentType ) { _serviceType = serviceType; _componentType = componentType; } void ICustomization.Customize( IFixture fixture ) { fixture.Customizations.Add( new OpenGenericsSpecimenBuilder( _serviceType, _componentType ) ); } class OpenGenericsSpecimenBuilder : ISpecimenBuilder { readonly Type _serviceType; readonly Type _componentType; public OpenGenericsSpecimenBuilder( Type serviceType, Type componentType ) { _serviceType = serviceType; _componentType = componentType; } object ISpecimenBuilder.Create( object request, ISpecimenContext context ) { var typedRequest = request as Type; if ( typedRequest != null && typedRequest.IsGenericType && typedRequest.GetGenericTypeDefinition() == _serviceType ) return context.Resolve( _componentType.MakeGenericType( typedRequest.GetGenericArguments().Single() ) ); return new NoSpecimen( request ); } } } } 

I assume that someone has a better implementation than this and / or has a built-in implementation.

EDIT: The following is an updated D using the sensing property:

 class D { readonly IOGF<C> _ogf; public D( IOGF<C> ogf ) { _ogf = ogf; } public IOGF<C> Ogf { get { return _ogf; } } } 
+7
source

AFICT there are no open generics in sight. D relies on IOGF<C> , which is a constructed type.

The error message is not related to open generators, but since IOGF<C> is an interface.

You can provide a mapping from IOGF<C> to OGF<C> as follows:

 fixture.Register<IOGF<C>>(() => fixture.CreateAnonymous<OGF<C>>()); 

Since OGF<C> relies on IX , you also need to specify a mapping X :

 fixture.Register<IX>(() => fixture.CreateAnonymous<X>()); 

That should do the trick.

However, as Nikos Baksevanis notes in his comment, if you use one of the three included auto-mosaic extensions, this will mostly work out of the box - for example,

 var fixture = new Fixture().Customize(new AutoMoqCustomization()); var d = fixture.CreateAnonymous<D>(); 
+4
source

All Articles