2012-04-05 27 views
6

Tôi đang tìm cách tùy chỉnh hành vi thời gian tạo của AutoFixture sao cho tôi có thể thiết lập một số đối tượng phụ thuộc sau khi các thuộc tính của lịch thi đấu đã được tạo và gán.AutoFixture có thể thực hiện một đại biểu tại thời điểm tạo đối tượng không?

Ví dụ, giả sử tôi có một phương pháp mà tùy một User vì sở hữu IsDeleted của nó luôn luôn phải là sai lầm cho một tập hợp một số bài kiểm tra:

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); 
} 

(Tôi tay một ObjectBuilder trở về thử nghiệm nên nó Tôi có thể tùy chỉnh thêm nếu cần.)

Điều tôi muốn làm là tự động liên kết người dùng đó với bộ sưu tập ẩn danh theo số Id vào thời điểm tạo, nhưng tôi không thể thực hiện việc này vì Id chưa được tạo ra bởi thời gian tôi tay giá trị trả lại cho phép kiểm thử đơn vị đúng. Dưới đây là loại điều tôi đang cố gắng làm:

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); 
      } 
} 

Có phải như thế này có thể không? Hoặc có lẽ có một cách tốt hơn để hoàn thành mục tiêu của tôi là tạo ra một đồ thị đối tượng ẩn danh?

+0

Bạn có muốn có một trường hợp cụ thể cho loại Người dùng và sử dụng lại giá trị thuộc tính Id của nó ở một nơi khác không? –

+2

Điều này có hữu ích không? http://stackoverflow.com/questions/5398258/customizing-autofixture-builder-with-seeded-property/5398653#5398653 –

+0

@MarkSeemann: 'Do()' có thể thực thi rõ ràng trước khi đối tượng được điền đầy đủ, do đó không ' t làm việc. Tôi có thể (và làm) bằng tay thực hiện lambda ví dụ hoặc làm tùy biến đối tượng của tôi sau khi tạo, nhưng tôi rất thích một thay thế tương tự như trên! – ladenedge

Trả lời

6

Đối với phương pháp Build, điều này là không thể và có thể sẽ không bao giờ có, bởi vì có nhiều tùy chọn tốt hơn có sẵn.

Trước hết, không bao giờ cần thiết phải viết các phương thức trợ giúp tĩnh xung quanh phương thức Build. Phương thức Build là cho các khởi tạo một lần thực sự, nơi người ta cần xác định các giá trị thuộc tính hoặc trường trước thực tế.

I.e. tưởng tượng một lớp học như thế này:

public class MyClass 
{ 
    private string txt; 

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

Trong này (giả tạo) Ví dụ, một thẳng fixture.CreateAnonymous<MyClass> sẽ ném vì nó sẽ cố gắng gán một cái gì đó khác hơn là "thanh" tới khách sạn.

Trong trường hợp một lần, người dùng có thể sử dụng phương pháp Build để tránh sự cố này. Một ví dụ đơn giản là để thiết lập giá trị rõ ràng để "thanh":

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

Tuy nhiên, thậm chí dễ dàng hơn là nên chỉ bỏ tài sản đó:

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

Tuy nhiên, một khi bạn bắt đầu muốn làm điều này nhiều lần , có những lựa chọn tốt hơn. AutoFixture có một công cụ rất tinh vi và tùy biến để xác định cách thức mọi thứ được tạo ra.

Như một sự khởi đầu, người ta có thể bắt đầu bằng cách di chuyển các thiếu sót của tài sản vào một tùy biến, như thế này:

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

Bây giờ, bất cứ khi nào cố định tạo ra một thể hiện của MyClass, nó chỉ sẽ bỏ tài sản đó hoàn toàn. Bạn vẫn có thể chỉ định một giá trị sau đó:

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

Nếu bạn muốn điều gì đó tinh vi hơn, bạn có thể implement a custom ISpecimenBuilder. Nếu bạn muốn chạy một số mã tùy chỉnh sau khi cá thể đã được tạo, bạn có thể trang trí ISpecimenBuilder của riêng bạn với một Bộ xử lý và cung cấp một đại biểu.Điều đó có thể trông giống như thế này:

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

(? BTW, là bạn vẫn còn trên AutoFixture 1.0 IIRC, chưa có một ObjectBuilder<T> xung quanh kể từ đó ...)

+0

Việc thêm một bộ xử lý sau có thể chỉ là vé và tôi đánh giá cao lời khuyên về việc sử dụng AF thích hợp. Cảm ơn bạn! (Về v1.0, vâng: chúng tôi có một số [rắc rối] (http://stackoverflow.com/questions/4650424/autofixture-2-with-isnt-working-as-it-did-in-autofixture-1) [ nâng cấp] (http://stackoverflow.com/questions/8595498/why-isnt-autofixture-working-with-the-stringlength-data-annotation) - có lẽ lần thứ ba sẽ là sự quyến rũ!) – ladenedge

3

Có một useful discussion on this topic on the AutoFixture CodePlex site.

Tôi tin rằng postprocessor Customization được liên kết ở đó sẽ giúp bạn. Ví dụ về cách sử dụng:

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 
Các vấn đề liên quan