AutoFixture: Configuring an Open Generics Specimen Builder

后端 未结 2 1319
忘掉有多难
忘掉有多难 2021-01-05 04:09

I have an object model that uses Open Generics (Yes, yes, now I have two problems; that\'s why I\'m here :) :-

public interface IOGF
{
}

class C
{
         


        
相关标签:
2条回答
  • 2021-01-05 04:24

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

    The error message isn't because of open generics, but because IOGF<C> is an interface.

    You can supply a mapping from IOGF<C> to OGF<C> like this:

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

    Since OGF<C> relies on IX you'll also need to supply a mapping to X:

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

    That should do the trick.

    However, as Nikos Baxevanis points out in his comment, if you use one of the three supplied auto-mocking extensions, this would basically work out of the box - e.g.

    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    var d = fixture.CreateAnonymous<D>();
    
    0 讨论(0)
  • 2021-01-05 04:31

    You could create a customization which 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 is implemented like so:

    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 someone has a better implementation than that though and/or there is a built-in implementation.

    EDIT: The following is the updated D with the sensing property:

    class D
    {
        readonly IOGF<C> _ogf;
    
        public D( IOGF<C> ogf )
        {
            _ogf = ogf;
        }
    
        public IOGF<C> Ogf
        {
            get { return _ogf; }
        }
    }
    
    0 讨论(0)
提交回复
热议问题