2013-08-10 11 views
34

Zanim zacznę, jestem wielkim fanem AutoFixture, wciąż jestem w stanie nauczyć się korzystać z tego narzędzia. Dziękuję za opracowanie Autofixture Mr Ploeh i wszystkich współpracowników.Nie mogę uchwycić różnicy między Freeze/Inject/Register

Zacznijmy od mojego pytania.

Według AutoFixture/AutoMoq ignores injected instance/frozen mock

Interesującą częścią powyższy link podany jest ten kod

Mock<ISettings> settingsMock = new Mock<ISettings>(); 
settingsMock.Setup(s => s.Get(settingKey)).Returns(xmlString); 

ISettings settings = settingsMock.Object; 
fixture.Inject(settings); 

Do którego Mark odpowiedź może być przepisany do

fixture.Freeze<Mock<ISettings>>() 
     .Setup(s => s.Get(settingKey)).Returns(xmlString); 

To wygląda jak cukier składniowy, za pomocą metody Freeze można pisać płynnie w interfejsie, tworząc makietę, konfigurację i iniekcję w pojemniku z autofixture.

Po przeprowadzeniu badań w Internecie, faktycznie istnieje różnica funkcjonalna między zamrożeniem a wstrzykiwaniem. Znalazłem to pytanie: https://github.com/AutoFixture/AutoFixture/issues/59 tym momencie odpowiedź How can I Freeze a null instance in AutoFixture

autor link powyżej opisać metodę zamrażania jak:

wewnętrznie, zamrożenie tworzy instancję żądanego typu (np IPayPalConfiguration), a następnie wstrzykuje go tak będzie zawsze wrócić tego wystąpienia, gdy poprosisz go ponownie

i under t i że gdy wykonamy

var customer = fixture.Freeze<Order>(); 

to zawsze użyje tego samego wystąpienia Zamówienia, gdy nasz kod zażąda typu Zamówienia. Ale co jeśli określę w konstruktorze Freeze, że chcę użyć konkretnej instancji?

Oto mały przykład kodu:

[Fact] 
public void MethodeName() 
{ 
    var fixture = new Fixture().Customize(new AutoMoqCustomization()); 
    fixture.Freeze<OrderLine>(new OrderLine("Foo")); 
    var order = fixture.Create<Order>(); 
} 

public class Order 
{ 
    private readonly OrderLine _line; 

    public Order(OrderLine line) 
    { 
     _line = line; 
    } 
} 
public class OrderLine 
{ 
    private readonly string _name; 

    public OrderLine(string name) 
    { 
     _name = name; 
    } 
} 

nie powinien nazwa Orderline być równy "Foo" zamiast namefe48163a-d5a0-49a5-b349-7b11ba5f804b? Dokumentacja metody Freeze mówi:

<typeparam name="T">The type to freeze.</typeparam> 
<param name="fixture">The fixture.</param> 
<param name="seed">Any data that adds additional information when creating the anonymous object. Hypothetically, this value might be the value being frozen, but this is not likely.</param> 

dlaczego autor nie jest pewien, kiedy zwracana jest wartość? Jeśli określę, moja instancja w konstruktorze Freeze, oczekuję, że autofixture użyje tej instancji?

następnie

Proszę zauważyć, że nie jest prawdopodobne, aby być stosowane jako wartości zamrożone, o ile nie zostaną dostosowane do tego celu. Jeśli chcesz wprowadzić konkretną wartość do Urządzenia, powinieneś użyć tej metody. "

Wygląda na to, że muszę dostosować parametr początkowy. Czy ktoś może wyjaśnić? Rozwiązaniem wskazanym w dokumentacji jest użycie metody Inject. I rzeczywiście działa w moim przykładzie kodu z OrderLine.

Poszukuję twojej pomocy w zrozumieniu różnicy między Freeze, Inject, a także Register, która zgodnie z kodem źródłowym jest wywoływana tylko metodą Inject, ale wymaga lambda.

Odpowiedz

34

rejestru i wstrzyknąć

Dawno nie było Inject i nie Freeze ; Register orzekł kod.

Wtedy było przeciążenie Register zdefiniowane wygląda następująco:

public static void Register<T>(this IFixture fixture, T item) 

jednak musiał udostępnić API z tego bliski krewny:

public static void Register<T>(this IFixture fixture, Func<T> creator) 

Twórca AutoFixture myślałem, że to było dobrze, ale niestety: użytkownicy byli zdezorientowani.Większość ciężko, użytkownik może napisać:

fixture.Register(() => universe.LightUp()); 

ale także

fixture.Register(universe.LightUp); 

co oznacza dokładnie to samo, bo universe.LightUp jest odniesienie do metody, a więc pasuje do delegata.

Jednak ta składnia wygląda jak odniesienie do właściwości, więc jeśli LightUp była właściwością zamiast metody, pierwsze przeciążenie zostałoby wybrane przez kompilator.

To spowodowało wiele zamieszania, więc przeciążenie Register<T>(this IFixture fixture, T item) zostało zmienione na Inject<T>(this IFixture fixture, T item).

Zamrożenie

Zamrożenie ma inną historię. Dawno, dawno temu, kiedy jeszcze wykorzystane AutoFixture w sposób imperatywny, zauważyłem, że wielokrotnie pisał kod tak:

var foo = fixture.Create<Foo>(); 
fixture.Inject(foo); 

Postanowiłem więc, że był to pomysł i nazwany zamarznie. Metoda Freeze jest tylko skrótem dla tych dwóch linii kodu.

Szukam twojej pomocy, aby zrozumieć różnicę między marzną wstrzyknąć, a także rejestracja, która zgodnie z kodem źródłowym, jest nazywany po prostu metodą Inject ale zajmuje lambda

Ogólnie rzecz biorąc, nie powinno być zbyt trudno rozróżnić między Inject i Register, ponieważ ich podpisy nie kolidują. Jeśli więc spróbujesz osiągnąć cel za pomocą jednej z tych dwóch metod, a Twój kod się skompiluje, prawdopodobnie wybrałeś odpowiednią wersję.

Byłoby to również w przypadku Freeze gdyby nie było na przeciążenia stosowanego w OP:

[EditorBrowsable(EditorBrowsableState.Never)] 
public static T Freeze<T>(this IFixture fixture, T seed) 

Zauważ, że to przeciążenie faktycznie ma EditorBrowsableState.Never, bo zawsze myli ludzi. Jednak pomimo tego, najwyraźniej ludzie wciąż znajdują to przeciążenie, więc myślę, że it should be moved in AutoFixture 4. To jedna z tych cech, które istnieją, ponieważ był łatwy do wdrożenia ...

+6

btw. jedną z przyczyn, dla których to przeciążenie wciąż się znajduje, może być fakt, że ReSharper wydaje się być ustawiony tak, aby domyślnie ignorował atrybut 'EditorBrowsable'. – TeaDrivenDev

+1

FWIW, w AutoFixture, planuję przenieść (lub zabrać) przeciążenia nasion od: https://github.com/AutoFixture/AutoFixture/issues/151 –

13

Freeze, Inject i Register wszystkie dostosowują algorytm tworzenia.

Z Inject i Register jesteś określając wyraźnie że obiekt powinien być utworzony w sposób szczególny w swoim przykład poprzez dostarczanie new OrderLine("Foo") ręcznie.

Nie podając , należy utworzyć obiekt - poprosić AutoFixture o dostarczenie instancji dla Ciebie.

W końcu wszystkie powyższe sposoby korzystają z tego samego dolnego poziomu API

fixture.Customize<T>(c => c.FromFactory(creator).OmitAutoProperties());


Powodem fixture.Freeze<OrderLine>(new OrderLine("Foo")); nie powoduje wystąpienie OrderLine o określonej wartości początkowe, ponieważ by default the seed is ignored .

faworyzować wartości materiału siewnego danego typu, można utworzyć SeedFavoringRelay<T>:

public class SeedFavoringRelay<T> : ISpecimenBuilder where T : class 
{ 
    public object Create(object request, ISpecimenContext context) 
    { 
     if (context == null) 
      throw new ArgumentNullException("context"); 

     var seededRequest = request as SeededRequest; 
     if (seededRequest == null || !seededRequest.Request.Equals(typeof(T))) 
      return new NoSpecimen(request); 

     var seed = seededRequest.Seed as T; 
     if (seed == null) 
      return new NoSpecimen(request); 

     return seed; 
    } 
} 

Wtedy może go używać jak poniżej:

fixture.Customizations.Add(
    new SeedFavoringRelay<OrderLine>()); 

fixture.Freeze<OrderLine>(new OrderLine("Foo")); 
// -> Now fixture.Create<Order>() creates an Order with OrderLine's Name = "Foo". 
+0

1 - ok to dlaczego następujący kod: Mock settingsMock = new Mock (); settingsMock.Setup (s => s.Get (klucz ustawień)). Zwraca (xmlString); fixture.Inject (settingsMock.Object); jest taki sam jak fixture.Freeze >(). Setup (s => s.Get (klucz ustawień)). Zwraca (xmlString); Według twojej wyjaśnienia Mówię autofixture stworzyć Mock gdy ktoś potrzebuje wystąpienie ISettings, ale część konfiguracji i zwraca część jest poza częścią zamrażać. Jak to jest, że jest również zamrożone? – Gui

+0

3 - Kiedy należy używać funkcji Wstrzyknij i zarejestruj? Jeśli AutoFixture proponuje te dwie publiczne metody, to pewnie są sytuacje, w których jedna działa lepiej od drugiej. – Gui

+0

Jak czytałem w [tych wpisach blogowych związanych z funkcją fotostabilności] (http://blog.ploeh.dk/tags.html # AutoFixture-ref), metoda 'Freeze' zwraca tę samą instancję T kiedykolwiek/gdziekolwiek konkretna instancja twojego' Urządzenia' musi zbudować dowolny obiekt. Te artykuły były dla mnie świetnym źródłem informacji. – Jeff

1

I zmodyfikowany test (który nie robi” t cokolwiek, BTW) i jeśli przejdziesz przez jego wykonanie, zobaczysz OrderLine z "Foo", ponieważ jego prywatna wartość _line jest wstrzykiwana do Order.

miałem inną wersję testu gdzie I dodaje właściwości tylko do odczytu dla OrderLine w Order i Name w OrderLine tak, że można zrobić twierdzeń o tych obiektach, ale to ani tu ani tam.

Test ten określa się na urządzenie przy użyciu metody FromFactory bezpośrednio, co może być pomocne czasami:

[Fact] 
public void MethodName() 
{ 
    var fixture = new Fixture().Customize(new AutoMoqCustomization()); 
    const string expected = "Foo"; 
    fixture.Customize<OrderLine>(o => o 
     .FromFactory(() => 
      new OrderLine(expected))); 
    var order = fixture.Create<Order>(); 
} 
Powiązane problemy