2012-04-05 22 views
6

Szukam dostosować zachowanie czasu tworzenia AutoFixture tak, że mogę ustawić niektóre obiekty zależne po tym, jak właściwości urządzenia zostały wygenerowane i przypisane.Czy AutoFixture może wykonać delegata w czasie tworzenia obiektu?

Na przykład załóżmy, że mam metodę, która dostosowuje User ponieważ jego IsDeleted nieruchomość zawsze musi być fałszywe dla pewnego zestawu testów:

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

(I ręka ObjectBuilder powrotem do testu, dlatego może w razie potrzeby dodatkowo dostosować urządzenie.)

Co chcę zrobić, to automatycznie skojarzyć tego użytkownika z kolekcją anonimową przez jego Id w czasie tworzenia, ale nie mogę tego zrobić tak jak jest, ponieważ Id nie ma zostały wygenerowane przez czas, który podaję zwracana wartość z powrotem do testu jednostkowego. Oto coś, co próbuję zrobić:

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

Czy coś takiego jest możliwe? A może jest lepszy sposób na osiągnięcie mojego celu, jakim jest stworzenie anonimowego wykresu obiektów?

+0

Czy chcesz mieć konkretną instancję dla typu użytkownika i ponownie użyć jej wartości właściwości Id w innym miejscu? –

+2

Czy to pomaga? http://stackoverflow.com/questions/5398258/customizing-autofixture-builder-with-seeded-property/5398653#5398653 –

+0

@MarkSeemann: 'Do()' może najwidoczniej wykonać zanim obiekt zostanie w pełni wypełniony, tak że doesn ' t działa. Mogę (i zrobię) ręcznie wykonać przykład lambda lub post-kreacji personalizacji obiektu, ale chciałbym alternatywy podobnej do powyższej! – ladenedge

Odpowiedz

6

Dla metody Build nie jest to możliwe i prawdopodobnie nigdy nie będzie, ponieważ dostępnych jest znacznie lepszych opcji.

Po pierwsze, nigdy nie powinno być konieczne pisanie statycznych metod pomocniczych przy metodzie Build. Metoda Build służy do naprawdę jednorazowych inicjalizacji, w których przed faktem należy zdefiniować wartość właściwości lub pola.

tj. wyobrazić sobie klasę tak:

public class MyClass 
{ 
    private string txt; 

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

W tym (contrived) przykład prosto fixture.CreateAnonymous<MyClass> zamierza wyrzucić, bo to będzie próbować przypisać coś innego niż „bar” do nieruchomości.

W scenariuszu jednorazowym można użyć metody Build, aby uniknąć tego problemu. Jednym z przykładów jest po prostu ustawić wartość wyraźnie do „bar”:

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

Jednak nawet łatwiej byłoby po prostu pominąć tę właściwość:

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

Jednak po uruchomieniu chcąc to zrobić wielokrotnie , są lepsze opcje. AutoFixture ma bardzo wyrafinowany i modyfikowalny silnik do definiowania tworzenia rzeczy.

Na początek można zacząć od przesuwając pominięcie nieruchomości do personalizacji, tak:

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

Teraz, gdy urządzenie tworzy instancję MojaKlasa, to po prostu się przejść tę właściwość całkowicie. Nadal można przypisać wartość potem:

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

Jeśli chcesz coś bardziej wyrafinowane, można implement a custom ISpecimenBuilder. Jeśli chcesz uruchomić niestandardowy kod po utworzeniu instancji, możesz udekorować swój własny ISpecimenBuilder za pomocą Postprocesora i dostarczyć delegata.To może wyglądać mniej więcej tak:

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

(? BTW, jesteś jeszcze na AutoFixture 1,0 IIRC, nie było to ObjectBuilder<T> wokół odtąd ...)

+0

Dodawanie postprocesora może być tylko biletem i doceniam porady dotyczące prawidłowego użycia AF. Dziękuję Ci! (W odniesieniu do wersji 1.0, tak: mieliśmy pewne problemy (http://stackoverflow.com/questions/4650424/autofixture-2-with-isnt-working-as-it-did-in-autofixture-1) [ uaktualnienie] (http://stackoverflow.com/questions/8595498/why-isnt-autofixture-working-z-tringlength-data-annotation) - może po raz trzeci będzie urok!) – ladenedge

3

Jest useful discussion on this topic on the AutoFixture CodePlex site.

Wierzę, że mój połączony tam postprocessor Customization powinien ci pomóc. Przykład użycia:

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 
Powiązane problemy