我试图克服一个类具有字符串构造函数参数的情况,该参数不能被Autofixture生成的任何旧字符串(Guid-y外观值)所满足。
在您想要简单地回复Mark Seemann's Ploeh blog entry on Convention-based Customizations的链接之前,请允许我说我已经参考了该测试的其他博客文章,我可以&#39 ; t得到通过。
当我在调试中单步执行时,我可以看到构造函数参数在某些时候传入了有效值,但测试仍然失败并带有Guid-y Color值。我认为这与这样一个事实有关,即有一种颜色'参数值,和 a'颜色'由Autofixture填充的属性。是不是我编写了一个解决构造函数参数的ISpecimenBuilder,但是我正在测试公共属性值(两个不同的东西)?
我知道所有这些对于该示例来说都是过度的,但我想到了一个更复杂的场景,其中使用Build<T>().With()
方法不会是DRY。
失败测试
[Fact]
public void Leaf_Color_Is_Brown()
{
// arrange
var fixture = new Fixture().Customize(new LeafColorCustomization());
// act
var leaf = fixture.Create<Leaf>();
// using .Build<>.With(), test passes
//var leaf = fixture.Build<Leaf>().With(l => l.Color, "brown").CreateAnonymous();
// assert
Assert.True(leaf.Color == "brown");
}
SUT
public class Leaf
{
public Leaf(string color)
{
if (color != "brown")
throw new ArgumentException(@"NO LEAF FOR YOU!");
this.Color = color;
}
public string Color { get; set; }
}
CompositeCustomization实现(我知道此示例中不需要AutoMoqCustomization())
public class LeafCustomization : CompositeCustomization
{
public LeafCustomization()
: base(
new LeafColorCustomization(),
new AutoMoqCustomization()) { }
}
特定于叶子的ICustomization
public class LeafColorCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
if (fixture == null)
throw new ArgumentNullException("fixture");
fixture.Customizations.Add(new LeafBuilder());
}
}
String-constructor-with-name-of-Color-specific ISpecimenBuilder
public class LeafBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var pi = request as ParameterInfo;
if (pi == null)
return new NoSpecimen(request);
if (pi.ParameterType != typeof(string) || pi.Name != "color")
return new NoSpecimen(request);
return "brown";
}
}
答案 0 :(得分:7)
解决方案1 :
注册Color
可写属性不应作为后处理的一部分分配任何自动值:
internal class LeafColorCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<Leaf>(c => c
.Without(x => x.Color));
fixture.Customizations.Add(new LeafBuilder());
}
}
解决方案2 :
将Color
属性设为只读:
public class Leaf
{
private readonly string color;
public Leaf(string color)
{
if (color != "brown")
throw new ArgumentException(@"NO LEAF FOR YOU!");
this.color = color;
}
public string Color
{
get { return this.color; }
}
}
由于Color
属性是只读的,因此AutoFixture不会为其分配值。
以上解决方案也适用于AutoFixture 2。
答案 1 :(得分:5)
假设你单独处理属性设置的东西,这里有一个构造函数参数限制Customization
,它可以解决这个问题:
class BrownLeavesCustomization : ICustomization
{
void ICustomization.Customize( IFixture fixture )
{
Func<string> notBrownGenerator = fixture.Create<Generator<string>>()
.SkipWhile( x => x == "Brown" )
.First;
fixture.Customizations.Add(
ArgumentGeneratorCustomization<Leaf>.ForConstructorArgument(
"color",
notBrownGenerator ) );
}
static class ArgumentGeneratorCustomization<T>
{
public static ISpecimenBuilder ForConstructorArgument<TArg>( string argumentName, Func<TArg> generator )
{
return new ConstructorArgumentGenerator<TArg>( argumentName, generator );
}
class ConstructorArgumentGenerator<TArg> : ISpecimenBuilder
{
readonly string _argumentName;
readonly Func<TArg> _generator;
public ConstructorArgumentGenerator( string argumentName, Func<TArg> generator )
{
Assert.Contains( argumentName, from ctor in typeof( T ).GetConstructors() from param in ctor.GetParameters() select param.Name );
_argumentName = argumentName;
_generator = generator;
}
object ISpecimenBuilder.Create( object request, ISpecimenContext context )
{
var pi = request as ParameterInfo;
if ( pi == null )
return new NoSpecimen( request );
if ( pi.Member.DeclaringType != typeof( T ) )
return new NoSpecimen( request );
if ( pi.Member.MemberType != MemberTypes.Constructor )
return new NoSpecimen( request );
if ( pi.ParameterType != typeof( TArg ) )
return new NoSpecimen( request );
if ( pi.Name != _argumentName )
return new NoSpecimen( request );
return _generator();
}
}
}
}
答案 2 :(得分:1)
解决方案:(基于Mark Seemann的comment on this answer)
在ISpecimenBuilder实现中容纳构造函数参数和可写属性,除了在LeafColorCustomization中添加LeafBuilder实例之外什么都不做:
public class LeafBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var paramInfo = request as ParameterInfo;
if (paramInfo != null
&& paramInfo.ParameterType == typeof(string)
&& paramInfo.Name == "color")
{ return "brown"; }
var propInfo = request as PropertyInfo;
if (propInfo != null
&& propInfo.PropertyType == typeof(string)
&& propInfo.Name == "Color")
{ return "brown"; }
return new NoSpecimen(request);
}
}
internal class LeafColorCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new LeafBuilder());
}
}