To pytanie dotyczy frameworka testów jednostkowych xUnit.net .
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 inicjalizacji i zakończenia, ale nie mogłem ich znaleźć.
Alternatywnie, jeśli programowo wywołuję 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 mi podpowiedzieć, jak deklaratywnie lub programowo uruchomić globalny kod konfiguracji / dezaktywacji?
Odpowiedzi:
O ile wiem, xUnit nie ma globalnego punktu rozszerzenia inicjalizacji / zakończenia. Jednak łatwo jest go utworzyć. Po prostu utwórz podstawową klasę testową, która implementuje
IDisposable
i wykonaj inicjalizację w konstruktorze oraz rozerwanie wIDisposable.Dispose
metodzie. 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 konfiguracja klasy bazowej i kod dezaktywacji będą wykonywane dla każdego wywołania. Może to nie być to, czego chcesz, ponieważ nie jest to zbyt wydajne. Bardziej zoptymalizowana wersja korzystałaby z
IClassFixture<T>
interfejsu w celu zapewnienia, że funkcja globalnej inicjalizacji / dezaktywacji jest wywoływana tylko raz. W tej wersji nie rozszerzasz klasy bazowej z klasy testowej, ale implementujeszIClassFixture<T>
interfejs, w którymT
odnosi się do Twojej klasy urządzeń: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 DummyTests(TestsFixture data) { } }
Spowoduje to, że konstruktor
TestsFixture
będzie uruchamiany tylko raz dla każdej testowanej klasy. Zależy to zatem od tego, co dokładnie chcesz wybrać między tymi dwiema metodami.źródło
[Collection("<name>")]
Szukałem tej samej odpowiedzi i w tej chwili dokumentacja xUnit jest bardzo pomocna w odniesieniu do tego, jak zaimplementować urządzenia klasowe i kolekcje, które zapewniają programistom szeroki zakres funkcji konfiguracji / rozłączania na poziomie klasy lub grupy klas. Jest to zgodne z odpowiedzią Geira Sagberga i zapewnia dobrą implementację szkieletu, aby zilustrować, jak powinien wyglądać.
https://xunit.github.io/docs/shared-context.html
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 { // ... }
źródło
Collection
atrybutem, aby wystąpiła konfiguracja „globalna”. Oznacza to, że jeśli masz coś, co chcesz skonfigurować przed uruchomieniem -any- test, musisz ozdobić -all- klasy testowe tym atrybutem. Moim zdaniem jest to zbyt kruche, ponieważ zapomnienie o udekorowaniu jednej klasy testowej może prowadzić do błędów, które są trudne do wyśledzenia. Byłoby miło, gdyby xUnit stworzył sposób na prawdziwie globalną konfigurację i porzucenie.Jest proste, łatwe rozwiązanie. Użyj wtyczki Fody.ModuleInit
https://github.com/Fody/ModuleInit
Jest to pakiet nuget i po zainstalowaniu dodaje nowy plik wywoływany
ModuleInitializer.cs
do projektu. Jest tutaj jedna metoda statyczna, która jest wklejana do zestawu po kompilacji i jest uruchamiana zaraz po załadowaniu zestawu i przed uruchomieniem.Używam tego do odblokowania licencji oprogramowania do zakupionej biblioteki. Zawsze zapominałem o odblokowaniu licencji w każdym teście, a nawet zapominałem o wyprowadzeniu testu z klasy bazowej, która ją odblokuje. Jasne iskry, które napisały tę bibliotekę, zamiast powiedzieć, że licencja została zablokowana, wprowadziły subtelne błędy numeryczne, które powodują, że testy kończą się niepowodzeniem lub zaliczają się, gdy nie powinny. Nigdy nie wiesz, czy poprawnie odblokowałeś bibliotekę, czy nie. Więc teraz wygląda jak inicjalizacja mojego modułu
/// <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"); } }
a wszystkie testy, które są umieszczane w tym zestawie, będą miały poprawnie odblokowaną licencję.
źródło
Aby udostępnić kod SetUp / TearDown między wieloma klasami, możesz użyć CollectionFixture xUnit .
Zacytować:
źródło