2015-11-13 15 views
5

Rozważmy następujące klasy:wstrzyknąć Zarówno interfejs i implementacja w AutoFixture

public interface IInterface {} 
public class Class : IInterface {} 

public class Customization : ICustomization 
{ 
    readonly IInterface item; 

    public Customization() : this(new Class()) {} 

    public Customization(IInterface item) 
    { 
     this.item = item; 
    } 

    public void Customize(IFixture fixture) 
    { 
     fixture.Inject(item); 
     var created = fixture.Create<Class>(); // Would like this to resolve as item from previous line. 
    } 
} 

Problem używam na to, że IInterface jest wstrzykiwany, podczas gdy Class nie jest. Czy istnieje sposób na wstrzyknięcie zarówno IInterface, jak i Class, aby zwrócić tę samą instancję dla obu?

Proszę zauważyć, że chciałbym to zrobić przy użyciu ICustomization (lub w ramach ICustomization), a nie z atrybutami metody testowej. Szukam zrobienia dostosowanego wtrysku na tych dwóch klasach. Jeśli użyję parametru [Frozen(Matching.ImplementedInterfaces)]Class item jako parametru, to nie zadziała, ponieważ klasa, która jest zamrożona, zastępuje wprowadzoną wartość w metodzie ICustomization.Customize.

Należy również pamiętać, że jest to przykładowy kod, a nie sposób, w jaki go używam. W metodzie badania xUnit chciałbym instancję Class który jest określony jako parametr być zamrożone IInstance powyżej:

public void MyTest(IInterface @interface, Class implementation) 
{ 
    Assert.Same(@interface, implementation); 
} 
+1

To przeciążenie 'Freeze' nie robi tego, co myślisz, że robi; patrz [dokumentacja] (https://github.com/AutoFixture/AutoFixture/blob/master/Src/AutoFixture/FixtureFreezer.cs#L43-L72). Zobacz odpowiedź Enrico Campidoglio na jeden dość idiomatyczny sposób osiągnięcia pożądanego rezultatu. Inną opcją byłoby użycie jednego z auto-szyderczych rozszerzeń kontenerów AutoFixture, które w zasadzie mają wbudowane takie funkcje. –

+0

Przepraszam @MarkSeemann, nie udaje mi się. Widziałem drugą dyskusję na temat Inject/Freeze i wpadłem w zakłopotanie. Miałem na myśli Inject, a nie Freeze i odpowiednio zaktualizowałem pytanie. –

Odpowiedz

0

OK to trwało wiecznie, aby dowiedzieć się, ale to pytanie/scenariusz jest ostatecznie ze względu na zły projekt z mojej strony i brak doświadczenia i uczenia się AutoFixture. Rzeczywistym scenariuszem tego, co próbowałem zrobić, było zarejestrowanie kontenera IoC z moim urządzeniem i chciałem, aby zarówno IServiceLocator, jak i jego implementacja zostały zarejestrowane w Urządzeniu, tak aby były dostępne do wstrzykiwania wartości do bieżącej metody testowej.

Ten problem rozwiązano, dodając a Customization do przekazywania żądań do podanego IServiceLocator (wspomnianego również here in this question).

Dodatkowo zrobiłem an extension method, który korzysta z Dynamitey, który robi to, czego szukałem, ale nie jest już używany i prawdopodobnie usunę go w pewnym momencie.

Więc odpowiedź brzmi: jeśli chce zrobić to z jakiegoś powodu, jesteś bardziej niż prawdopodobne doing it wrong. Jestem w połowie kuszony, aby usunąć tę odpowiedź, ale zostawię ją tutaj, na wypadek, gdyby inny nowicjusz, taki jak ja, spotkał się z tym samym problemem i mógł z tego skorzystać.

Na koniec chciałbym podziękować @ enrico-campidoglio i @ mark-seemann za ich cierpliwość i naprawdę pouczające/jakościowe odpowiedzi/pomoc (a także dla osób, które nie głosują na to pytanie). Wiem, że pasek jest ustawiony wysoko tutaj @ Stack Overflow i prawdopodobnie powinienem był mieć trochę więcej cierpliwości przed opublikowaniem tego pytania.

2

Oczywiście, można zastosować atrybut [Frozen] na betonowej parametru klasy i określić ImplementedInterfaces jak dopasowywanie kryteria:

[Theory, AutoData] 
public void Test(
    [Frozen(Matching.ImplementedInterfaces)]Class implementation, 
    IInterface @interface) 
{ 
    Assert.Same(implementation, @interface); 
} 

to mówi AutoFixture zapewnić samąClass instancji za każdym razem musi stworzyć wartość dla dowolnym z jej wdrożenia ed interfejsy.

+0

Dziękuję za odpowiedź, @ enrico-campidoglio. Przepraszam za brak jasności, szukam Inject, a nie Freeze. Co więcej, czekam na Inject w ramach ICustomization, a nie na poziomie atrybutów. Zaktualizowałem pytanie, by było bardziej zwięzłe/dokładne. Przepraszam za moją nowość! –

4

Jeśli koniecznie musi to zrobić samemu

Jeśli przyjrzeć się bliżej metody Inject, można zauważyć, że jest to rzeczywiście sposób ogólny, ale że rodzaj argumentu wywnioskować kiedy go używać jak go używać . Jeśli chcesz zamrozić oba, możesz - po prostu musisz wywołać Inject dla każdego typu.

Oto lekko zmodyfikowany Customization. W celu zapobieżenia ewentualnie nieprawidłowy przygnębiony, zmieniłem go tak, aby jego item pole (a odpowiadający item konstruktor argument) jest typu Class:

public class Customization : ICustomization 
{ 
    readonly Class item; 

    public Customization() : this(new Class()) { } 

    public Customization(Class item) 
    { 
     this.item = item; 
    } 

    public void Customize(IFixture fixture) 
    { 
     fixture.Inject(item); 
     fixture.Inject<IInterface>(item); 
    } 
} 

Uwaga, Customize wstrzykuje ten sam element dwa razy. W pierwszym wierszu argument typu ogólnego jest określany przez kompilator jako Class, podczas gdy w drugim wierszu argument typu IInterface jest zdefiniowany jawnie.

W tej realizacji, następujący test przechodzi:

[Fact] 
public void UseCustomization() 
{ 
    var fixture = new Fixture().Customize(new Customization()); 

    var c = fixture.Create<Class>(); 
    var i = fixture.Create<IInterface>(); 

    Assert.Equal(c, i); 
} 

Za pomocą wbudowanego w API

Wszystko, co powiedział, to uważam, że łatwiej jest po prostu użyć wbudowanej API:

[Fact] 
public void UseTypeRelay() 
{ 
    var fixture = new Fixture(); 
    fixture.Customizations.Add(
     new TypeRelay(
      typeof(IInterface), 
      typeof(Class))); 
    fixture.Freeze<Class>(); 

    var c = fixture.Create<Class>(); 
    var i = fixture.Create<IInterface>(); 

    Assert.Equal(c, i); 
} 

TypeRelay mapy IInterface do Class, co oznacza, że ​​wszystkie wnioski o IInterface zostanie przekazana do wniosków f lub Class.Gdy zamrożone jest Class, oznacza to, że nie tylko jest zamrożone, ale także . Tak samo jest .

Powiązane problemy