2013-03-20 77 views
11

我想克服一個場景,其中一個類有一個字符串構造函數參數,它不能被Autofixture(Guid-y的值)生成的任何舊字符串滿足。如何在ICustomization中使用Autofixture(v3),ISpecimenBuilder處理構造函數參數?

在你試圖簡單地回答Mark Seemann's Ploeh blog entry on Convention-based Customizations的鏈接之前,讓我說我一直在引用它和他的這個測試的其他博客條目,這是我無法通過的。

當我在調試中逐步完成時,我可以看到在某些時候構造函數參數傳入了有效值,但測試仍然失敗,出現Guid-y Color值。我認爲這與「顏色」參數值是由「自動混合」填充的「顏色」屬性有關。是我寫了一個ISpecimenBuilder來解決構造函數參數,但我正在測試公共屬性值(兩種不同的東西)?

我知道這一切都是矯枉過正的例子,但我想象一個更復雜的情況下,使用Build<T>().With()方法不會幹。

失敗的測試

[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的構造函數與 - 名色特有的最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"; 
     } 
    } 

回答

7

解決方案1 ​​

註冊Color可寫屬性不應該b È分配任何自動的值作爲後處理的一部分:

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。

+0

輝煌 - 工程! Re:解決方案#1,可以/應該指定跳過'ISpecimenBuilder'('LeafBuilder')內部的'Color'可寫屬性,而不是'ICustomization'('LeafColorCustomization')內部?我的印象是許多'.Without()'警告會污染執行,並且它會更乾淨(特別是在一個更復雜的例子中)除了一系列'fixture.Customizations.Add(新的XXXBuilder( ));'ICustomization'內部 - 我不知道不同的定製屬於哪裏? – Jeff 2013-03-20 21:33:50

+3

@Lumirris你肯定可以在'LeafBuilder'中處理這個問題。你只需要處理'request'是一個'PropertyInfo'的情況,除了你已經處理了'ParameterInfo'的地方。 – 2013-03-21 09:45:51

+0

@MarkSeemann - [YAGNI](http://en.wikipedia.org/wiki/You_aren't_gonna_need_it)問題儘管如此,在考慮更復雜的場景時,最好從'ISpecimenBuilder'實現'LeafBuilder'內處理,使用'ICustomization'實現只添加所有必需的'ISpecimenBuilder'實現? – Jeff 2013-03-21 17:20:44

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(); 
      } 
     } 
    } 
} 
+0

@NikosBaxevanis我在這裏的真正原因是看它是否會引起你的反應等等:)知道AF本身或公共可用的任何類似結構?任何改進想法? – 2013-03-21 14:50:35

1

解決方案:(基於馬克西曼的comment on this answer

容納ISpecimenBuilder實現中的構造函數參數和可寫屬性,除了將LeafBuilder實例添加到LeafColorCustomization:

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()); 
    } 
} 
相關問題