2012-10-19 13 views
37

To pytanie dotyczy ramy testowania jednostkowego xUnit.net.xUnit.net: Globalna konfiguracja + rozpad?

Muszę uruchomić kod przed wykonaniem jakiegokolwiek testu, a także kod po wykonaniu wszystkich testów. Pomyślałem, że powinien istnieć jakiś interfejs atrybutów lub znaczników wskazujący globalny kod inicjujący i kończący, ale nie mógł ich znaleźć.

Ewentualnie, jeśli modlę xUnit programowo, mogę również osiągnąć to, co chcę z następującego kodu:

static void Main() 
{ 
    try 
    { 
     MyGlobalSetup(); 
     RunAllTests(); // What goes into this method? 
    } 
    finally 
    { 
     MyGlobalTeardown(); 
    } 
} 

Czy ktoś może podać mi wskazówkę na temat sposobu deklaratywnie lub programowo uruchomić jakiś globalny setup/kod przerywaniem ?

+1

Chyba tu jest odpowiedź: http://stackoverflow.com/questions/12379949/xunit-resharper-how-to-run-setup-code-only-once –

Odpowiedz

44

O ile mi wiadomo, xUnit nie ma globalnego punktu rozszerzenia inicjalizacji/rozpadu. Jednak łatwo jest go utworzyć. Po prostu stwórz podstawową klasę testową, która implementuje IDisposable i wykonaj inicjalizację w konstruktorze i rozpadzie w metodzie IDisposable.Dispose. Wyglądałoby to tak:

public abstract class TestsBase : IDisposable 
{ 
    protected TestsBase() 
    { 
     // Do "global" initialization here; Called before every test method. 
    } 

    public void Dispose() 
    { 
     // Do "global" teardown here; Called after every test method. 
    } 
} 

public class DummyTests : TestsBase 
{ 
    // Add test methods 
} 

Jednak kod i kod podstawowej klasy podstawowej zostaną wykonane dla każdego połączenia. To może nie być to, czego chcesz, ponieważ nie jest bardzo wydajne. Bardziej zoptymalizowana wersja będzie korzystała z interfejsu IClassFixture<T>, aby zapewnić, że globalna funkcja inicjowania/odrzucania jest wywoływana tylko raz. W tej wersji, nie rozszerza klasy bazowej z klasy testowej ale implementować interfejs IClassFixture<T> gdzie T odnosi się do klasy urządzenia:

using Xunit; 

public class TestsFixture : IDisposable 
{ 
    public TestsFixture() 
    { 
     // Do "global" initialization here; Only called once. 
    } 

    public void Dispose() 
    { 
     // Do "global" teardown here; Only called once. 
    } 
} 

public class DummyTests : IClassFixture<TestsFixture> 
{ 
    public void SetFixture(TestsFixture data) 
    { 
    } 
} 

Ten będzie wynik w konstruktorze TestsFixture tylko jest prowadzony raz dla każdej klasy w trakcie testu. To zależy od tego, co chcesz dokładnie wybrać między tymi dwoma metodami.

+3

Wygląda na to, że IUseFixture już nie istnieje, ponieważ został zastąpiony przez IClassFixture. – GaTechThomas

+1

Chociaż to działa, myślę, że CollectionFixture w odpowiedzi od Geira Sagberga lepiej pasuje do tego scenariusza, ponieważ został specjalnie zaprojektowany do tego celu. Nie musisz też dziedziczyć swoich klas testowych, po prostu zaznacz je atrybutem "[Collection (" ")]' – MichelZ

+0

Czy jest jakikolwiek sposób konfiguracji asynchronizacji i zagubienia? – Andrii

-3

Należy uruchomić opcję bootstrap, aby uruchomić kod przed wszystkim innym. Przynajmniej jest dla PHPUnit. Nie wspomniałeś o tym, z którego struktury korzystasz.

Nie wiem, w jaki sposób uruchomić kod po wszystkich testach. Ale możesz to zrobić w skrypcie wrappera, który uruchamia testy w pierwszej kolejności, a następnie uruchamia wszystko, czego potrzebujesz.

+0

Tytuł pytanie tag mają xUnit w im. – Codism

+1

@Codism: "xUnit" zwykle odnosi się do rodziny zestawów testów jednostkowych dla różnych języków. Są rodziną, ponieważ wszystkie zostały wymodelowane po JUnit i mają bardzo podobną strukturę. Zobacz http://en.wikipedia.org/wiki/XUnit, lub po prostu umieść kursor nad tagiem "xunit" dodanym do Twojego pytania. Wygląda na to, że używasz "xUnit", platformy testowej .NET, ale ta konkretna jest dość nowa i nieznana. – DXM

+1

@DXM: dziękuję za wyjaśnienia dotyczące xunit. Zaktualizuję moje pytanie. – Codism

7

Istnieje proste łatwe rozwiązanie. Użyj wtyczki Fody.ModuleInit

https://github.com/Fody/ModuleInit

Jest to pakiet Nuget i kiedy go zainstalować dodaje się nowy plik o nazwie ModuleInitializer.cs do projektu. Jest tu jedna metoda statyczna, która zostaje wpleciona w zespół po kompilacji i jest uruchamiana zaraz po wczytaniu złożenia i zanim wszystko zostanie uruchomione.

Używam tego, aby odblokować licencję na oprogramowanie w bibliotece, którą kupiłem. Zawsze zapominałem, aby odblokować licencję w każdym teście, a nawet zapomnieć o wypróbowaniu testu z klasy bazowej, która go odblokuje. Jasne iskierki, które napisały tę bibliotekę, zamiast powiedzieć ci, że była zamknięta w licencji, wprowadziły subtelne błędy numeryczne, które powodują, że testy zawodzą lub mijają się, gdy nie powinny. Nigdy nie dowiesz się, czy poprawnie odblokowałeś bibliotekę, czy nie.Tak więc mój init modułów wygląda tak, jakby wyglądał jak jeden moduł init i wszystkie testy, które zostaną umieszczone w tym zestawie, będą miały dla niego poprawnie odblokowaną licencję.

+2

Solidny pomysł; Niestety, wydaje się, że nie działa jeszcze z testami jednostkowymi DNX. –

5

Aby udostępnić kod SetUp/TearDown między wieloma klasami, możesz użyć xUnit's CollectionFixture.

Cytat:

Aby użyć oprawy kolekcja, trzeba podjąć następujące kroki:

  • Tworzenie klasy osprzętu i umieścić kod starcie w konstruktorze klasy osprzętu.
  • Jeśli klasa urządzenia musi wykonać czyszczenie, zaimplementuj IDisposable na klasie urządzenia i umieść kod oczyszczania w metodzie Dispose() .
  • Utwórz klasę definicji kolekcji, dekorując ją za pomocą atrybutu [CollectionDefinition], nadając jej niepowtarzalną nazwę, która będzie oznaczać kolekcję testową przez .
  • Dodaj ICollectionFixture <> do klasy definicji kolekcji.
  • Dodaj atrybut [Collection] do wszystkich klas testowych, które będą częścią kolekcji, używając unikatowej nazwy podanej w teście klasy kolekcji [CollectionDefinition] klasy definicji.
  • Jeśli klasy testowe wymagają dostępu do egzemplarza urządzenia, dodaj je jako argument konstruktora, a zostanie ono dostarczone automatycznie.
15

Jestem trochę późno do partii, ale szukałem tej samej odpowiedzi, a w tym czasie dokumentacja xUnit jest bardzo pomocne w odniesieniu do sposobu wdrożenia klasy uchwyty i Urządzeń Collection, które dają programistom szeroki zakres funkcji konfiguracji/przestoju na poziomie klasy lub grupy klas. Jest to zgodne z odpowiedzią Geira Sagberga i daje dobrą implementację szkieletu, aby zilustrować, jak powinien wyglądać.

https://xunit.github.io/docs/shared-context.html

Oprawy Kolekcja Kiedy stosować: gdy chcesz utworzyć jeden kontekst testowy i udostępnić go wśród testów w kilku klasach testowych i miały je oczyścić po wszystkich testach w klasach testowych Zakończyliśmy.

Czasami chcesz udostępnić obiekt urządzenia spośród wielu klas testowych. Przykład bazy danych używany dla urządzeń klas jest świetnym przykładem: możesz zainicjować bazę danych za pomocą zestawu danych testowych, a następnie pozostawić te dane testowe do wykorzystania przez wiele klas testowych. Możesz użyć funkcji fixture kolekcji xUnit.net, aby udostępnić pojedynczą instancję obiektu wśród testów w kilku klasach testowych.

Aby korzystać z urządzeń do zbierania, należy podjąć następujące kroki:

Tworzenie klasy osprzętu i umieścić kod starcie w konstruktorze klasy osprzętu. Jeśli klasa urządzenia wymaga wyczyszczenia, zaimplementuj IDisposable na klasie urządzenia i umieść kod oczyszczający w metodzie Dispose(). Utwórz klasę definicji kolekcji, dekorując ją atrybutem [CollectionDefinition], nadając jej unikalną nazwę, która będzie identyfikować kolekcję testów. Dodaj ICollectionFixture <> do klasy definicji kolekcji. Dodaj atrybut [Collection] do wszystkich klas testowych, które będą częścią kolekcji, używając unikalnej nazwy podanej w atrybucie klasy Collection Collection definicji [CollectionDefinition]. Jeśli klasy testowe wymagają dostępu do instancji urządzenia, dodaj ją jako argument konstruktora, a zostanie ona dostarczona automatycznie. Oto prosty przykład:

public class DatabaseFixture : IDisposable 
{ 
    public DatabaseFixture() 
    { 
     Db = new SqlConnection("MyConnectionString"); 

     // ... initialize data in the test database ... 
    } 

    public void Dispose() 
    { 
     // ... clean up test data from the database ... 
    } 

    public SqlConnection Db { get; private set; } 
} 

[CollectionDefinition("Database collection")] 
public class DatabaseCollection : ICollectionFixture<DatabaseFixture> 
{ 
    // This class has no code, and is never created. Its purpose is simply 
    // to be the place to apply [CollectionDefinition] and all the 
    // ICollectionFixture<> interfaces. 
} 

[Collection("Database collection")] 
public class DatabaseTestClass1 
{ 
    DatabaseFixture fixture; 

    public DatabaseTestClass1(DatabaseFixture fixture) 
    { 
     this.fixture = fixture; 
    } 
} 

[Collection("Database collection")] 
public class DatabaseTestClass2 
{ 
    // ... 
} 

xUnit.net traktuje opraw poboru w taki sam sposób jak uchwyty klasy, z wyjątkiem, że czas życia obiektu osprzętu kolekcja jest już: to jest tworzony przed jakimkolwiek testy są przeprowadzane w dowolnej z klas testowych w kolekcji i nie zostaną wyczyszczone, dopóki wszystkie klasy testów w kolekcji nie zostaną zakończone.

Kolekcje testowe można również ozdobić za pomocą IClassFixture <>. xUnit.net traktuje to tak, jakby każda indywidualna klasa testowa w kolekcji testowej była dekorowana oprzyrządowaniem klasowym.

Kolekcje testowe mają również wpływ na sposób, w jaki xUnit.net uruchamia testy podczas równoległego ich działania. Aby uzyskać więcej informacji, zobacz Testowanie równoległe.

Ważna uwaga: Lampy muszą być w tym samym zestawie, co test, który ich używa.

+0

"Kolekcje testowe można również ozdobić za pomocą IClassFixture <>. XUnit.net traktuje to tak, jakby każda indywidualna klasa testowa w kolekcji testowej była dekorowana klasą." Czy mam jakąś szansę? Nie bardzo to rozumiem. –

+0

@TannerFaulkner Oprawa klasowa była sposobem na ustawienie i rozbicie poziomu klasy, tak jak w przypadku tradycyjnego projektu testowego jednostki .net, gdy jest używana metoda inicjowania testu: [TestInitialize] public void Initialize() { –

+0

Errr ....: -/Powinienem był napisać [ClassInitialze] w powyższym komentarzu. –