在 AutoFixture 中注入接口和实现
Inject Both Interface and Implementation in AutoFixture
考虑以下 类:
public interface IInterface {}
public class Class : IInterface {}
public class Customization : ICustomization
{
readonly IInterface item;
public Customization() : this( new Class() ) {}
public Customization( IInterface item )
{
this.item = item;
}
public void Customize( IFixture fixture )
{
fixture.Inject( item );
var created = fixture.Create<Class>(); // Would like this to resolve as item from previous line.
}
}
我 运行 遇到的问题是 IInterface
被注入了,而 Class
没有。有没有办法同时注入 IInterface
和 Class
以便为两者返回相同的实例?
请注意,我想使用 ICustomization
(或在 ICustomization
中)而不是测试方法中的属性来执行此操作。我希望对这两个 类 进行自定义注入。如果我使用 [Frozen( Matching.ImplementedInterfaces)]Class item
作为参数,它不起作用,因为冻结的 Class 会覆盖 ICustomization.Customize
方法中的注入值。
另外请注意,这是示例代码,而不是我的使用方式。在 xUnit 测试方法中,我希望指定为参数的 Class
实例是上面冻结的 IInstance
:
public void MyTest( IInterface @interface, Class implementation )
{
Assert.Same( @interface, implementation );
}
当然,您可以在具体的 class 参数上应用 [Frozen]
属性,并指定 ImplementedInterfaces
作为匹配条件:
[Theory, AutoData]
public void Test(
[Frozen(Matching.ImplementedInterfaces)]Class implementation,
IInterface @interface)
{
Assert.Same(implementation, @interface);
}
这告诉 AutoFixture 每次必须为它的 any 创建值时提供 same Class
实例实现的接口。
如果你绝对必须自己动手
如果仔细观察 Inject
方法,您会注意到它实际上是一个泛型方法,但是当您像使用它一样使用它时会推断出类型参数。如果你想冻结两者,你可以 - 你只需要为每种类型调用 Inject
。
此处稍作修改 Customization
。为了防止可能无效的向下转换,我对其进行了更改,使其 item
字段(以及相应的 item
构造函数参数)的类型为 Class
:
public class Customization : ICustomization
{
readonly Class item;
public Customization() : this(new Class()) { }
public Customization(Class item)
{
this.item = item;
}
public void Customize(IFixture fixture)
{
fixture.Inject(item);
fixture.Inject<IInterface>(item);
}
}
请注意 Customize
两次注入相同的项目。在第一行中,泛型类型参数被编译器推断为 Class
,而在第二行中,类型参数 IInterface
被显式定义。
鉴于此实施,以下测试通过:
[Fact]
public void UseCustomization()
{
var fixture = new Fixture().Customize(new Customization());
var c = fixture.Create<Class>();
var i = fixture.Create<IInterface>();
Assert.Equal(c, i);
}
使用内置 API
综上所述,我认为简单地使用内置 API:
[Fact]
public void UseTypeRelay()
{
var fixture = new Fixture();
fixture.Customizations.Add(
new TypeRelay(
typeof(IInterface),
typeof(Class)));
fixture.Freeze<Class>();
var c = fixture.Create<Class>();
var i = fixture.Create<IInterface>();
Assert.Equal(c, i);
}
TypeRelay
将 IInterface
映射到 Class
,这意味着对 IInterface
的所有请求都将中继到对 Class
的请求。当 Class
被冻结时,这意味着不仅 Class
被冻结,而且 IInterface
.
也被冻结
好的,这花了很长时间才弄明白,但这 question/scenario 最终是由于我的设计不佳以及对 AutoFixture 缺乏经验和学习。我尝试做的实际场景是在我的夹具中注册一个 IoC 容器,我希望 IServiceLocator 及其实现都在夹具中注册,以便它们可用于将值注入当前测试方法。
已通过添加 a Customization for relaying requests to a provided IServiceLocator (also mentioned ) 解决此问题。
此外,我确实制作了 an extension method that makes use of Dynamitey 来满足我的需求,但它已不再使用,我可能会在某个时候删除它。
所以答案是:如果您出于某种原因想要这样做,您很有可能 doing it wrong。我中途想删除这个答案,但我会把它留在这里,以防其他像我这样的新手遇到同样的问题并可能从中受益。
最后,我要感谢@enrico-campidoglio 和@mark-seemann 的耐心,真的 informative/quality answers/assistance(也感谢没有人对这个问题投反对票)。我知道这里的门槛很高@Stack Overflow,在发布这个问题之前我可能应该多一点耐心。
考虑以下 类:
public interface IInterface {}
public class Class : IInterface {}
public class Customization : ICustomization
{
readonly IInterface item;
public Customization() : this( new Class() ) {}
public Customization( IInterface item )
{
this.item = item;
}
public void Customize( IFixture fixture )
{
fixture.Inject( item );
var created = fixture.Create<Class>(); // Would like this to resolve as item from previous line.
}
}
我 运行 遇到的问题是 IInterface
被注入了,而 Class
没有。有没有办法同时注入 IInterface
和 Class
以便为两者返回相同的实例?
请注意,我想使用 ICustomization
(或在 ICustomization
中)而不是测试方法中的属性来执行此操作。我希望对这两个 类 进行自定义注入。如果我使用 [Frozen( Matching.ImplementedInterfaces)]Class item
作为参数,它不起作用,因为冻结的 Class 会覆盖 ICustomization.Customize
方法中的注入值。
另外请注意,这是示例代码,而不是我的使用方式。在 xUnit 测试方法中,我希望指定为参数的 Class
实例是上面冻结的 IInstance
:
public void MyTest( IInterface @interface, Class implementation )
{
Assert.Same( @interface, implementation );
}
当然,您可以在具体的 class 参数上应用 [Frozen]
属性,并指定 ImplementedInterfaces
作为匹配条件:
[Theory, AutoData]
public void Test(
[Frozen(Matching.ImplementedInterfaces)]Class implementation,
IInterface @interface)
{
Assert.Same(implementation, @interface);
}
这告诉 AutoFixture 每次必须为它的 any 创建值时提供 same Class
实例实现的接口。
如果你绝对必须自己动手
如果仔细观察 Inject
方法,您会注意到它实际上是一个泛型方法,但是当您像使用它一样使用它时会推断出类型参数。如果你想冻结两者,你可以 - 你只需要为每种类型调用 Inject
。
此处稍作修改 Customization
。为了防止可能无效的向下转换,我对其进行了更改,使其 item
字段(以及相应的 item
构造函数参数)的类型为 Class
:
public class Customization : ICustomization
{
readonly Class item;
public Customization() : this(new Class()) { }
public Customization(Class item)
{
this.item = item;
}
public void Customize(IFixture fixture)
{
fixture.Inject(item);
fixture.Inject<IInterface>(item);
}
}
请注意 Customize
两次注入相同的项目。在第一行中,泛型类型参数被编译器推断为 Class
,而在第二行中,类型参数 IInterface
被显式定义。
鉴于此实施,以下测试通过:
[Fact]
public void UseCustomization()
{
var fixture = new Fixture().Customize(new Customization());
var c = fixture.Create<Class>();
var i = fixture.Create<IInterface>();
Assert.Equal(c, i);
}
使用内置 API
综上所述,我认为简单地使用内置 API:
[Fact]
public void UseTypeRelay()
{
var fixture = new Fixture();
fixture.Customizations.Add(
new TypeRelay(
typeof(IInterface),
typeof(Class)));
fixture.Freeze<Class>();
var c = fixture.Create<Class>();
var i = fixture.Create<IInterface>();
Assert.Equal(c, i);
}
TypeRelay
将 IInterface
映射到 Class
,这意味着对 IInterface
的所有请求都将中继到对 Class
的请求。当 Class
被冻结时,这意味着不仅 Class
被冻结,而且 IInterface
.
好的,这花了很长时间才弄明白,但这 question/scenario 最终是由于我的设计不佳以及对 AutoFixture 缺乏经验和学习。我尝试做的实际场景是在我的夹具中注册一个 IoC 容器,我希望 IServiceLocator 及其实现都在夹具中注册,以便它们可用于将值注入当前测试方法。
已通过添加 a Customization for relaying requests to a provided IServiceLocator (also mentioned
此外,我确实制作了 an extension method that makes use of Dynamitey 来满足我的需求,但它已不再使用,我可能会在某个时候删除它。
所以答案是:如果您出于某种原因想要这样做,您很有可能 doing it wrong。我中途想删除这个答案,但我会把它留在这里,以防其他像我这样的新手遇到同样的问题并可能从中受益。
最后,我要感谢@enrico-campidoglio 和@mark-seemann 的耐心,真的 informative/quality answers/assistance(也感谢没有人对这个问题投反对票)。我知道这里的门槛很高@Stack Overflow,在发布这个问题之前我可能应该多一点耐心。