自动固定:配置开放式泛型样本生成器

本文关键字:样本 泛型 开放式 配置 | 更新日期: 2023-09-27 18:30:42

我有一个使用开放泛型的对象模型(是的,是的,现在我有两个问题;这就是我在这里:)的原因:-

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

我正在尝试让AutoFixture生成上述D的匿名实例。但是,AutoFixture本身没有内置的策略来构建IOGF<>因此我们观察到:

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

基本消息是:

Ploeh.AutoFixture.ObjectCreationException : AutoFixture 无法从 IOGF'1[C] 创建实例,很可能是因为它没有公共构造函数,是一种抽象或非公共类型。

我很高兴为它提供一个具体的实现:

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

和关联的绑定:

fixture.Register<IX,X>();

如何(或者我什至应该以这种方式看待问题??)使以下测试通过?

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's IOGF<C> requirement
        Assert.NotNull( fixture.CreateAnonymous<D>());
    }
}

(在 codeplex 站点上有关于此的讨论和问题 - 我只需要快速暗示这一点,如果这只是一个坏主意和/或我错过了什么,我愿意删除它)

编辑2:(另见对马克的回答的评论)这里的(公认是人为的)上下文是对大型"几乎完整的系统"被测系统对象图的验收测试,而不是一个小的(受控/易于摸索的:)单元或集成测试方案中的类对或三元组。正如自我问题括号中提到的,我不完全相信这种类型的测试甚至有意义。

自动固定:配置开放式泛型样本生成器

您可以创建一个自定义,其工作原理如下:

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

并像这样实现:

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

我假设有人有比这更好的实现和/或有一个内置的实现。

编辑:以下是具有传感特性的更新D:

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

AFICT 看不到开放的泛型。 D依赖于构造类型IOGF<C>

错误消息不是因为打开泛型,而是因为IOGF<C>是一个接口。

您可以提供从IOGF<C>OGF<C>的映射,如下所示:

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

由于OGF<C>依赖于IX因此您还需要提供映射到X

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

这应该可以解决问题。

然而,正如 Nikos Baxevanis 在他的评论中指出的那样,如果您使用提供的三个自动模拟扩展之一,这基本上可以开箱即用 - 例如

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