2012-04-05 32 views
6

我期待定製AutoFixture的創建時行爲,以便在生成並分配燈具的屬性後,我可以設置一些相關對象。AutoFixture可以在創建對象時執行委託嗎?

例如,假設我有一個自定義的方法的User,因爲它的IsDeleted屬性總是有成爲一組特定的測試失敗:

public class User 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public bool IsDeleted { get; set; } 
} 

public static ObjectBuilder<User> BuildUser(this Fixture f) 
{ 
    return f.Build<User>().With(u => u.IsDeleted, false); 
} 

(我手的ObjectBuilder回測試,因此如果需要,可以進一步定製夾具。)

我想在創建時自動將該用戶與其匿名集合Id關聯起來,但我不能這樣做,因爲Id還沒有由我手中產生的時間返回值返回到單元測試的適當位置。這是我想要做的事情:

public static ObjectBuilder<User> BuildUserIn(this Fixture f, UserCollection uc) 
{ 
    return f.Build<User>() 
      .With(u => u.IsDeleted, false); 
      .AfterCreation(u => 
      { 
       var relation = f.Build<UserCollectionMembership>() 
           .With(ucm => ucm.UserCollectionId, uc.Id) 
           .With(ucm => ucm.UserId, u.Id) 
           .CreateAnonymous(); 
       Repository.Install(relation); 
      } 
} 

是這樣的可能嗎?或者有更好的方法來實現我創建匿名對象圖的目標?

+0

你想爲用戶類型的特定實例,並重新使用它的ID屬性值別的地方? – 2012-04-05 19:17:35

+2

這有幫助嗎? http://stackoverflow.com/questions/5398258/customizing-autofixture-builder-with-seeded-property/5398653#5398653 – 2012-04-05 20:32:26

+0

@MarkSeemann:'Do()'顯然可以在對象完全填充之前執行,這樣就不會'工作。我可以(也可以)手動執行示例lambda或做我的對象定製後創建,但我喜歡類似於上面的替代! – ladenedge 2012-04-09 07:31:52

回答

6

對於Build方法,這是不可能的,也許永遠不會,因爲有更好的選擇可用。

首先,應該永遠不需要圍繞Build方法編寫靜態輔助方法。 Build方法用於在事實之前需要定義屬性或字段值的真正的一次性初始化。

I.e.想象一類是這樣的:

public class MyClass 
{ 
    private string txt; 

    public string SomeWeirdText 
    { 
     get { return this.txt; } 
     set 
     { 
      if (value != "bar") 
       throw new ArgumentException(); 
      this.txt = value; 
     } 
    } 
} 

在此(做作)例如,直fixture.CreateAnonymous<MyClass>是要扔掉,因爲它會嘗試給比「巴」的財產以外的東西。

在一次性場景中,可以使用Build方法來解決此問題。一個例子是簡單明確的值設置爲「欄」:

var mc = 
    fixture.Build<MyClass>().With(x => x.SomeWeirdText, "bar").CreateAnonymous(); 

然而,更容易將只是忽略該屬性:

var mc = 
    fixture.Build<MyClass>().Without(x => x.SomeWeirdText).CreateAnonymous(); 

然而,一旦你開始想重複做,有更好的選擇。 AutoFixture有一個非常複雜和可定製的引擎,用於定義事物的創建方式。

作爲一個開始,一個可以由酒店的遺漏移動到一個定製的,像這樣開始:

fixture.Customize<MyClass>(c => c.Without(x => x.SomeWeirdText)); 

現在,每當夾具創建MyClass的實例,它只是要跳過該屬性共。您仍然可以在之後分配一個值:

var mc = fixture.CreateAnonymous<MyClass>(); 
my.SomeWeirdText = "bar"; 

如果您想要更復雜一些的東西,您可以implement a custom ISpecimenBuilder。如果您希望在創建實例後運行一些自定義代碼,則可以使用Postprocessor修飾您自己的ISpecimenBuilder並提供一個委託。這可能是這個樣子:

fixture.Customizations.Add(
    new Postprocessor(yourCustomSpecimenBuilder, obj => 
     { */ do something to obj here */ })); 

(?順便說一句,你還在上AutoFixture 1.0 IIRC,還沒有從那以後是一個ObjectBuilder<T>左右...)

+0

添加一個後處理器可能只是票據,我很欣賞正確的AF使用建議。謝謝! (關於v1.0,是的:我們有一些[麻煩](http://stackoverflow.com/questions/4650424/autofixture-2-with-isnt-working-as-it-did-in-autofixture-1)[升級](http://stackoverflow.com/questions/8595498/why-isnt-autofixture-working-with-the-stringlength-data-annotation) - 也許第三次將是魅力!) – ladenedge 2012-04-12 21:17:28

3

有一個useful discussion on this topic on the AutoFixture CodePlex site

我相信我那裏的postprocessor Customization應該可以幫到你。用法示例:

class AutoControllerDataAttribute : AutoDataAttribute 
{ 
    public AutoControllerDataAttribute() 
     : this(new Fixture()) 
    { 
    } 

    public AutoControllerDataAttribute(IFixture fixture) 
     : base(fixture) 
    { 
     fixture.Customize(new AutoMoqCustomization()); 
     fixture.Customize(new ApplyControllerContextCustomization()); 
    } 

    class ApplyControllerContextCustomization : PostProcessWhereIsACustomization<Controller> 
    { 
     public ApplyControllerContextCustomization() 
      : base(PostProcess) 
     { 
     } 

     static void PostProcess(Controller controller) 
     { 
      controller.FakeControllerContext(); 
      // etc. - add stuff you want to happen after the instance has been created 
相關問題