xUnit.net: Global setup + tearddown?

To pytanie dotyczy struktury testów jednostkowych xUnit.net .

Muszę uruchomić jakiś kod przed wykonaniem testu, a także jakiś kod po wykonaniu wszystkich testów. Myślałem, że powinien istnieć jakiś interfejs atrybutów lub znaczników, który wskazywałby globalny kod inicjalizacji i zakończenia, ale nie mogłem ich znaleźć.

Alternatywnie, jeśli wywołam programowo xUnit, mogę również osiągnąć to, co chcę za pomocą następującego kodu:

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

Czy ktoś może dostarczyć mi podpowiedź jak deklaratywnie lub programowo uruchomić jakiś globalny kod setup / teardown?

Author: Lukas Kabrt, 2012-10-19

4 answers

Z tego co wiem, xUnit nie ma globalnego punktu inicjalizacji/teardown extension point. Jednak łatwo go stworzyć. Po prostu utwórz podstawową klasę testową, która implementuje IDisposable i wykonaj inicjalizację w konstruktorze, a tearddown 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że, konfiguracja klasy bazowej i Kod teardown będą wykonywane dla każdego wywołania. To może nie być to, czego chcesz, ponieważ nie jest to bardzo skuteczne. Bardziej zoptymalizowana wersja używałaby IClassFixture<T> interfejs zapewniający, że globalna funkcja inicjalizacji/teardown jest wywoływana tylko raz. W tej wersji nie rozszerzasz klasy bazowej z klasy testowej, ale implementujesz interfejs IClassFixture<T>, Gdzie T odnosi się do twojej klasy fixture:

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

To spowoduje, że konstruktor TestsFixture zostanie uruchomiony tylko raz dla każdej klasy w trakcie testu. Zależy to zatem od tego, co chcesz dokładnie wybrać między tymi dwiema metodami.

 59
Author: Erik Schierboom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-07 05:01:13

Szukałem tej samej odpowiedzi i w tej chwili dokumentacja xUnit jest bardzo pomocna w implementacji opraw klasowych i opraw kolekcji, które dają programistom szeroki zakres funkcjonalności konfiguracji/odrywania 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 Kolekcji Kiedy używać: gdy chcesz utworzyć pojedynczy kontekst testu i udostępnić go testom w kilku klasach testowych i wyczyścić go po zakończeniu wszystkich testów w klasach testowych.

Czasami będziesz chciał udostępnić obiekt fixture między wieloma klasami testowymi. Przykład bazy danych używany dla oprawy klas jest doskonałym przykładem: możesz chcieć zainicjować bazę danych zestawem danych testowych, a następnie pozostawić te dane testowe na miejscu do użycia przez wiele zajęcia testowe. Możesz użyć funkcji oprawy kolekcji xUnit.net aby współdzielić pojedynczą instancję obiektu między testami w kilku klasach testowych.

Aby korzystać z urządzeń kolekcji, musisz wykonać następujące kroki:

Utwórz klasę fixture i umieść kod startowy w konstruktorze klasy fixture. Jeśli Klasa fixture musi wykonać czyszczenie, zaimplementuj IDisposable na klasie fixture i umieść kod czyszczenia w metodzie Dispose (). Utwórz kolekcję definition class, dekorując go atrybutem [CollectionDefinition], nadając mu unikalną nazwę, która będzie identyfikować testową kolekcję. 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 do atrybutu [CollectionDefinition] klasy definicji kolekcji testowej. Jeśli klasy testowe potrzebują dostępu do instancji fixture, dodaj ją jako konstruktor argument, a zostanie on podany 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 oprawy kolekcji w taki sam sposób jak oprawy klas, z tym wyjątkiem, że żywotność obiektu oprawy kolekcji jest dłuższa: jest on tworzony przed uruchomieniem jakichkolwiek testów w którejkolwiek z klas testowych w kolekcji i nie zostanie wyczyszczony, dopóki wszystkie klasy testowe w kolekcji nie skończą działać.

Kolekcje testowe mogą być również ozdobione IClassFixture. xUnit.net traktuje to tak, jakby każda klasa testowa w kolekcji testowej była ozdobiona oprawą klasową.

Zbiory testowe wpływają również na sposób xUnit.net uruchamia testy podczas ich równoległego uruchamiania. Aby uzyskać więcej informacji, zobacz równoległe Uruchamianie testów.

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

 19
Author: Larry Smith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-20 13:48:56

Jest proste, łatwe rozwiązanie. Użyj Fody.ModuleInit plugin

Https://github.com/Fody/ModuleInit

Jest to pakiet nuget i po zainstalowaniu dodaje nowy plik o nazwie ModuleInitializer.cs do projektu. Jest tutaj jedna statyczna metoda, która zostaje wpleciona w zespół po zbudowaniu i jest uruchamiana natychmiast po załadowaniu zespołu i zanim cokolwiek zostanie uruchomione.

Używam tego do odblokowania licencji oprogramowania do zakupionej biblioteki. Zawsze zapominałam aby odblokować licencję w każdym teście, a nawet zapomnieć o wyprowadzeniu testu z klasy bazowej, która by go odblokowała. Jasne iskry, które napisały tę bibliotekę, zamiast powiedzieć ci, że została zablokowana licencja, wprowadziły subtelne błędy numeryczne, które powodują, że testy nie powiodą się lub przechodzą, gdy nie powinny. nigdy nie dowiesz się, czy poprawnie odblokowałeś bibliotekę, czy nie. Więc teraz mój moduł init wygląda jak

/// <summary>
/// Used by the ModuleInit. All code inside the Initialize method is ran as soon as the assembly is loaded.
/// </summary>
public static class ModuleInitializer
{
    /// <summary>
    /// Initializes the module.
    /// </summary>
    public static void Initialize()
    {
            SomeLibrary.LicenceUtility.Unlock("XXXX-XXXX-XXXX-XXXX-XXXX");
    }
}

I wszystkie testy, które zostaną umieszczone w tym zestawie, będą miały odblokowaną licencję poprawnie dla nich.

 9
Author: bradgonesurfing,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-26 12:10:58

Aby współdzielić Kod SetUp / TearDown pomiędzy wieloma klasami, możesz użyć CollectionFixture xUnit.

Cytat:

Aby korzystać z urządzeń kolekcji, musisz wykonać następujące kroki:

  • Utwórz klasę fixture i umieść kod startowy w konstruktorze klasy fixture.
  • jeśli Klasa fixture musi wykonać czyszczenie, zaimplementuj IDisposable na klasie fixture i umieść kod czyszczenia w Dispose() metoda.
  • Utwórz klasę definicji kolekcji, dekorując ją atrybutem [CollectionDefinition], nadając jej unikalną nazwę, która będzie określić zbiór 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 teście atrybut [CollectionDefinition] klasy definicji kolekcji.
  • jeśli klasy testowe potrzebujesz dostępu do instancji fixture, dodaj ją jako argument konstruktora, a zostanie ona dostarczona automatycznie.
 7
Author: Geir Sagberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-04 07:40:38