2013-05-24 16 views
6

Poprzednio zadawałem podobny question na SO, do którego dostałem odpowiedź. W tym czasie, ze względów praktycznych, mechanicznie zastosowałem odpowiedź, ale teraz próbuję zrozumieć, w jaki sposób mechanizm deklaratywnego ustawienia urządzenia jest.Jakie są zasady dotyczące deklaratywnego sposobu ustawiania urządzenia przez AutoFixture?

Tak więc obecnie patrzę na Mark Seemann's Dealing With Types Without Public Constructors blog post i konwertuję ją na deklaratywną. Jest bardzo podobny do mojego pierwotnego zapytania, ale nie mogę go uruchomić. Należy pamiętać, że podany kod nie jest w rzeczywistości kodem produkcyjnym i jest to ćwiczenie do nauki.

Teraz, jeśli to nie pomaga, Mam imperative code się na GitHub, a kod w pytaniu jest przedstawiony poniżej:

[Fact] 
public static void CanOverrideCtorArgs() 
{ 
    var fixture = new Fixture(); 

    var knownText = "This text is not anonymous"; 
    fixture.Register<int, IMyInterface>(i => new FakeMyInterface(i, knownText)); 

    var sut = fixture.Create<MyClass>(); 
} 

Jest to kod podobny do przedstawionych w tej post.

W związku z tym moje pytanie jest, co powinienem wiedzieć/przeczytać, aby przekształcić ten fragment imperatywnego kodu na deklaratywny.

Odpowiedz

6

Go czytać

interesujących przykładów dostosowań i sposobu pakowania, wymieszać i mieszają je.

Główna zasada polega na tym, że dostosowania są tak szczegółowe, jak to możliwe.

Następnie trzeba je nakarmić w rurociągu przetwarzania przez albo:

  • AutoData pochodzące z A atrybuty globalnej rzeczy (tzn jak MyTestConventions w odpowiedzi Marka)
  • CustomizeWith pomocnika [1] lub podobny
  • Oszustwa takie jak robi [Freeze(As ...)]

Moje wykonanie

Automatyzacja to, bym napisać:

[Theory, AutoData] 
public static void OutOfBandCustomization( 
    [CustomizeWith(typeof(MyFakerCustomization))] MyClass sut) 
{ 
} 

Stosując tę ​​dostosowanie:

public class MyFakerCustomization : ICustomization 
{ 
    void ICustomization.Customize(IFixture fixture) 
    { 
     var knownText = "This text is not anonymous"; 
     fixture.Register<int, IMyInterface>(i => 
      new FakeMyInterface(i, knownText)); 
    } 
} 

Oczywiście rejestracji string i/lub za pomocą AutoMoqCustomization może również być przydatne.

Moje ogólne pomocników

[1] CustomizeWith jest to pomocnik Atrybut (wskazówka kapelusz do Adam Jasinski):

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)] 
sealed class CustomizeWithAttribute : CustomizeAttribute 
{ 
    readonly Type _type; 

    public CustomizeWithAttribute(Type customizationType) 
    { 
     if (customizationType == null) 
      throw new ArgumentNullException("customizationType"); 
     if (!typeof(ICustomization).IsAssignableFrom(customizationType)) 
      throw new ArgumentException( 
       "Type needs to implement ICustomization"); 
     _type = customizationType; 
    } 

    public override ICustomization GetCustomization(ParameterInfo parameter) 
    { 
     return (ICustomization)Activator.CreateInstance(_type); 
    } 
} 

Poza

Jedna wskazówka: można wyrazić

fixture.Register<int, IMyInterface>(i => 
    new FakeMyInterface(i, knownText)); 

jako

fixture.Customize<IMyInterface>(c =>c.FromFactory((int i)=> 
    new FakeMyInterface(i,knownText))); 

też. Chociaż nie ułatwia to twojej sprawy, jest to bardziej ogólny sposób dostosowywania tego, co się dzieje.

Wewnętrznie Register jest [obecnie]:

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

Dziękuję Ruben. Przyjrzę się Twojej odpowiedzi i odpowiednio zaktualizuję OP. – DavidS

+0

Doskonała odpowiedź i rozwiązanie! – DavidS

6

Przede wszystkim mam zamiar odpowiedzieć na to pytanie, przy założeniu, że TypesWithoutPublicCtrs jest defined as in the OP's GitHub repository:

public class TypesWithoutPublicCtrs 
{ 
    private readonly IMyInterface _mi; 

    public TypesWithoutPublicCtrs(IMyInterface mi) 
    { 
     _mi = mi; 
    } 
} 

Powodem Ja wyraźnie nazywając to się tak dlatego, że nazwa jest czerwony śledź: to ma mają publicznego konstruktora; po prostu nie ma konstruktora domyślnego .

W każdym razie AutoFixture łatwo radzi sobie z brakiem domyślnych konstruktorów. Problemem tutaj nie jest sama klasa TypesWithoutPublicCtrs, ale raczej interfejs IMyInterface. Interfejsy są problematyczne, ponieważ w ogóle nie można ich zainicjować.

Tak więc, musisz jakoś zmapować interfejs do konkretnej klasy. Można to zrobić na różne sposoby.

jednorazowe rozwiązanie

Raz na jakiś czas używam tej jednorazowe rozwiązanie, chociaż uważam, że to brzydkie. Jest to jednak łatwe i nie wymaga skomplikowanej konfiguracji.

[Theory, AutoData] 
public void TestSomething(
    [Frozen(As = typeof(IMyInterface))]FakeMyInterface dummy, 
    TypesWithoutPublicCtrs sut) 
{ 
    // use sut here, and ignore dummy 
} 

To nie jest szczególnie miłe, ponieważ opiera się na efekt uboczny atrybutu [Frozen], ale działa jako samodzielny jednorazowej rozwiązania.

Konwencja

Jednak ja dużo raczej jak zrobić konwencję z niego, tak że sama konwencja ma zastosowanie do wszystkich testów w zestaw testowy. Test z użyciem takiej konwencji może wyglądać następująco:

[Theory, MyTestConventions] 
public void TestSomething(TypesWithoutPublicCtrs sut) 
{ 
    // use sut here; it'll automatically have had FakeMyInterface injected 
} 

Atrybut [MyTestConventions] mógłby wyglądać następująco:

public class MyTestConventionsAttribute : AutoDataAttribute 
{ 
    public MyTestConventionsAttribute() : 
     base(new Fixture().Customize(new MyTestConventions()) 
    {} 
} 

Klasa MyTestConventions musi implementować interfejs ICustomization.Istnieje kilka sposobów mapowania IMyInterface do FakeMyInterface; tutaj jest jeden:

public class MyTestConventions : ICustomization 
{ 
    public void Customize(IFixture fixture) 
    { 
     fixture.Customizations.Add(
      new TypeRelay(typeof(IMyInterface), typeof(FakeMyInterface))); 
    } 
} 

Auto-Mocking

Można jednak znudzi konieczności tworzenia i utrzymywania tych wszystkich Fakes, więc można również włączyć AutoFixture się z Auto-Mocking Container. Istnieją różne opcje wykonania tego, leveraging Moq, NSubstitute, FakeItEasy i Rhino Mocks.

Powiązane problemy