我之前曾在SO上问过类似的question,我得到了答案。当时,为了方便起见,我机械地应用了答案,但现在我正试图弄清楚声明性地设置灯具的机制是什么。
所以,我目前正在查看Mark Seemann's Dealing With Types Without Public Constructors blog post并将其转换为声明性的。它与我原来的查询非常相似,但我无法让它工作。请注意,给出的代码实际上不是生产代码,这是一个学习练习。
现在如果它有帮助,我在GitHub上有imperative code,有问题的代码转载如下:
[Fact]
public static void CanOverrideCtorArgs()
{
var fixture = new Fixture();
var knownText = "This text is not anonymous";
fixture.Register<int, IMyInterface>( i => new FakeMyInterface( i, knownText ) );
var sut = fixture.Create<MyClass>();
}
这是与此post中给出的代码类似的代码。
因此,我的问题是我应该知道/阅读什么才能将这段命令式代码转换为声明式。
答案 0 :(得分:6)
首先,我将在假设TypesWithoutPublicCtrs
为defined as in the OP's GitHub repository的情况下回答这个问题:
public class TypesWithoutPublicCtrs
{
private readonly IMyInterface _mi;
public TypesWithoutPublicCtrs(IMyInterface mi)
{
_mi = mi;
}
}
我明确地说出来的原因是因为这个名字是一个红色的鲱鱼: 有一个公共构造函数;它只是没有默认构造函数。
无论如何,AutoFixture很容易处理默认构造函数的缺失。这里的问题不是TypesWithoutPublicCtrs
类本身,而是IMyInterface
接口。接口是有问题的,因为它们根本无法初始化。
因此,您需要以某种方式将接口映射到具体类。有很多方法可以做到这一点。
一次性解决方案
有一段时间,我会使用这种一次性的解决方案,虽然我发现它很难看。但是,它很简单,不需要很多复杂的设置。
[Theory, AutoData]
public void TestSomething(
[Frozen(As = typeof(IMyInterface))]FakeMyInterface dummy,
TypesWithoutPublicCtrs sut)
{
// use sut here, and ignore dummy
}
这不是特别好,因为它依赖于[Frozen]
属性的副作用,但它可以作为一个独立的一次性解决方案。
<强>公约强>
但是,我更喜欢用它来制定约定,因此相同的约定适用于测试套件中的所有测试。使用这种惯例的测试可能如下所示:
[Theory, MyTestConventions]
public void TestSomething(TypesWithoutPublicCtrs sut)
{
// use sut here; it'll automatically have had FakeMyInterface injected
}
[MyTestConventions]
属性可能如下所示:
public class MyTestConventionsAttribute : AutoDataAttribute
{
public MyTestConventionsAttribute() :
base(new Fixture().Customize(new MyTestConventions())
{}
}
MyTestConventions
类必须实现接口ICustomization
。您可以通过多种方式将IMyInterface
映射到FakeMyInterface
;这是一个:
public class MyTestConventions : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(
new TypeRelay(typeof(IMyInterface), typeof(FakeMyInterface)));
}
}
自动嘲讽强>
但是,您可能已经厌倦了必须创建和维护所有这些伪造,因此您也可以将AutoFixture转换为Auto-Mocking Container。这样做有多种选择,leveraging Moq,NSubstitute,FakeItEasy和Rhino Mocks。
答案 1 :(得分:6)
去阅读
有关自定义的很好示例以及如何打包,混合和混合它们。
主要原则是您尽可能精确地进行自定义。
然后,您需要通过以下方式将它们输入处理管道:
AutoData
- 全局内容的派生属性(例如Mark的回答中的MyTestConventions
)CustomizeWith
帮助者[1]或类似的[Freeze( As ... )]
自动化,我写道:
[Theory, AutoData]
public static void OutOfBandCustomization(
[CustomizeWith( typeof( MyFakerCustomization ) )] MyClass sut )
{
}
使用此自定义:
public class MyFakerCustomization : ICustomization
{
void ICustomization.Customize( IFixture fixture )
{
var knownText = "This text is not anonymous";
fixture.Register<int, IMyInterface>( i =>
new FakeMyInterface( i, knownText ) );
}
}
显然,注册string
和/或使用AutoMoqCustomization
也可能有用。
[1] CustomizeWith
是这个辅助属性(帽子提示为Adam Jasinski):
[AttributeUsage( AttributeTargets.Parameter, AllowMultiple = true )]
sealed class CustomizeWithAttribute : CustomizeAttribute
{
readonly Type _type;
public CustomizeWithAttribute( Type customizationType )
{
if ( customizationType == null )
throw new ArgumentNullException( "customizationType" );
if ( !typeof( ICustomization ).IsAssignableFrom( customizationType ) )
throw new ArgumentException(
"Type needs to implement ICustomization" );
_type = customizationType;
}
public override ICustomization GetCustomization( ParameterInfo parameter )
{
return (ICustomization)Activator.CreateInstance( _type );
}
}
一个提示:你可以表达
fixture.Register<int, IMyInterface>( i =>
new FakeMyInterface( i, knownText ) );
作为
fixture.Customize<IMyInterface>(c =>c.FromFactory((int i)=>
new FakeMyInterface(i,knownText)));
太。虽然这不会使您的案例更容易,但这是一种更通用的方式来定制正在发生的事情。
在内部,Register
是[目前]:
fixture.Customize<T>(c => c.FromFactory(creator).OmitAutoProperties());