我有一个使用Open Generics的对象模型(是的,是的,现在我有两个问题;这就是为什么我在这里:): -
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 :(另请参阅Mark的回答评论)这里(公认的设计)上下文是对一个大的“几乎完整的系统”系统测试对象图而不是一个小的(受控/易于理解:)的验收测试单元或集成测试场景中的类或三元组。正如在自我问题括号中所提到的那样,我并不完全相信这种类型的测试甚至有意义。
答案 0 :(得分:7)
您可以创建自定义,其工作方式如下:
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; }
}
}
答案 1 :(得分:4)
AFICT看不到开放的仿制药。 D
依赖IOGF<C>
这是一种构造类型。
错误消息不是因为打开泛型,而是因为IOGF<C>
是一个接口。
您可以IOGF<C>
从OGF<C>
提供a mapping,如下所示:
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>();