Czy powinienem zainicjować pola klas przy takiej deklaracji?
public class SomeTest extends TestCase
{
private final List list = new ArrayList();
public void testPopulateList()
{
// Add stuff to the list
// Assert the list contains what I expect
}
}
Lub w setUp () w ten sposób?
public class SomeTest extends TestCase
{
private List list;
@Override
protected void setUp() throws Exception
{
super.setUp();
this.list = new ArrayList();
}
public void testPopulateList()
{
// Add stuff to the list
// Assert the list contains what I expect
}
}
Zwykle używam pierwszego formularza, ponieważ jest bardziej zwięzły i pozwala mi używać końcowych pól. Jeśli nie muszę używać metody setUp () do konfiguracji, czy nadal powinienem jej używać i dlaczego?
Wyjaśnienie:
JUnit utworzy instancję klasy testowej raz na metodę testową. Oznacza to, list
że zostanie utworzony raz na test, niezależnie od tego, gdzie to zadeklaruję. Oznacza to również, że między testami nie ma zależności czasowych. Wygląda więc na to, że używanie setUp () nie ma żadnych zalet. Jednak JUnit FAQ zawiera wiele przykładów, które inicjują pustą kolekcję w setUp (), więc myślę, że musi być jakiś powód.
Odpowiedzi:
Jeśli zastanawiasz się konkretnie nad przykładami w JUnit FAQ, takimi jak podstawowy szablon testu , myślę, że najlepszą praktyką jest to, że testowana klasa powinna być utworzona w metodzie setUp (lub w metodzie testowej) .
Kiedy przykłady JUnit tworzą ArrayList w metodzie setUp, wszystkie przechodzą do testowania zachowania tej ArrayList, z przypadkami takimi jak testIndexOutOfBoundException, testEmptyCollection i tym podobne. Istnieje perspektywa kogoś, kto pisze zajęcia i upewnia się, że wszystko działa prawidłowo.
Prawdopodobnie powinieneś zrobić to samo podczas testowania własnych klas: stwórz swój obiekt w setUp lub w metodzie testowej, abyś mógł uzyskać rozsądny wynik, jeśli później go zepsujesz.
Z drugiej strony, jeśli używasz klasy kolekcji Java (lub innej klasy biblioteki, jeśli o to chodzi) w swoim kodzie testowym, prawdopodobnie nie dzieje się tak dlatego, że chcesz ją przetestować - to tylko część osprzętu testowego. W takim przypadku możesz spokojnie założyć, że działa zgodnie z przeznaczeniem, więc zainicjowanie go w deklaracji nie będzie problemem.
Ale warto, pracuję na dość dużej, kilkuletniej bazie kodu stworzonej przez TDD. Zwykle inicjalizujemy rzeczy w ich deklaracjach w kodzie testowym, a przez półtora roku, kiedy byłem nad tym projektem, nigdy nie spowodowało to problemu. Tak więc istnieją przynajmniej anegdotyczne dowody na to, że jest to rozsądne rozwiązanie.
źródło
Zacząłem kopać sam i znalazłem jedną potencjalną zaletę używania
setUp()
. Jeśli podczas wykonywania programusetUp()
zostaną zgłoszone , JUnit wydrukuje bardzo pomocny ślad stosu. Z drugiej strony, jeśli wyjątek zostanie zgłoszony podczas tworzenia obiektu, komunikat o błędzie po prostu mówi, że JUnit nie był w stanie utworzyć instancji przypadku testowego i nie widzisz numeru wiersza, w którym wystąpił błąd, prawdopodobnie dlatego, że JUnit używa odbicia do utworzenia wystąpienia testu zajęcia.Nic z tego nie dotyczy przykładu tworzenia pustej kolekcji, ponieważ to nigdy się nie wyrzuci, ale jest to zaleta
setUp()
metody.źródło
Oprócz odpowiedzi Alexa B.
Wymagane jest nawet użycie metody setUp w celu utworzenia wystąpienia zasobów w określonym stanie. Wykonanie tego w konstruktorze jest nie tylko kwestią czasu, ale ze względu na sposób, w jaki JUnit przeprowadza testy, każdy stan testowy zostałby usunięty po uruchomieniu.
JUnit najpierw tworzy wystąpienia klasy testClass dla każdej metody testowej i rozpoczyna wykonywanie testów po utworzeniu każdej instancji. Przed uruchomieniem metody testowej uruchamiana jest jej metoda konfiguracji, w której można przygotować pewien stan.
Gdyby stan bazy danych został utworzony w konstruktorze, wszystkie instancje utworzyłyby stan bazy danych bezpośrednio po sobie, przed uruchomieniem każdego testu. Od drugiego testu testy będą działać w stanie brudnym.
Cykl życia JUnits:
Przy niektórych logach w teście z dwiema metodami testowymi otrzymujesz: (liczba to kod skrótu)
źródło
@BeforeClass
w JUnit 4.W JUnit 4:
@Before
metodzie, aby wychwycić awarie.final
, dokładnie tak, jak podano w pytaniu,@Before
, aby złapać awarie.@BeforeClass
, ale bądź ostrożny na zależności między testami.Inicjalizacja w
@Before
metodzie lub metodzie testowej pozwala uzyskać lepsze raportowanie błędów w przypadku awarii. Jest to szczególnie przydatne do tworzenia instancji Class Under Test (którą można zepsuć), ale jest również przydatne do wywoływania systemów zewnętrznych, takich jak dostęp do systemu plików („nie znaleziono pliku”) lub połączenie z bazą danych („odmowa połączenia”).Jest to dopuszczalne , aby mieć standard prosty i zawsze używaj
@Before
(oczywiste błędy, ale gadatliwy) lub zawsze inicjować w deklaracji (zwięzły, ale daje błędy mylące), ponieważ skomplikowane zasady kodowania są trudne do naśladowania, a to nie jest wielka sprawa.Inicjalizacja w programie
setUp
jest reliktem JUnit 3, w którym wszystkie instancje testowe były chętnie inicjowane, co powoduje problemy (szybkość, pamięć, wyczerpanie zasobów) w przypadku kosztownej inicjalizacji. Dlatego najlepszą praktyką było wykonanie kosztownej inicjalizacji w programiesetUp
, która była uruchamiana dopiero po wykonaniu testu. To już nie obowiązuje, więc jest znacznie mniej koniecznesetUp
.To podsumowuje kilka innych odpowiedzi, które pogrzebują lede, zwłaszcza Craiga P. Motlina (samo pytanie i odpowiedź własna), Moss Collum (klasa poddawana testowi) i dsaff.
źródło
W JUnit 3 inicjatory pól będą uruchamiane raz na metodę testową przed uruchomieniem jakichkolwiek testów . Tak długo, jak wartości pól są małe w pamięci, zajmują niewiele czasu na konfigurację i nie wpływają na stan globalny, używanie inicjatorów pól jest technicznie w porządku. Jednakże, jeśli to się nie uda, może skończyć się zużyciem dużej ilości pamięci lub czasu na konfigurację pól przed uruchomieniem pierwszego testu, a nawet może zabraknąć pamięci. Z tego powodu wielu programistów zawsze ustawia wartości pól w metodzie setUp (), gdzie jest to zawsze bezpieczne, nawet jeśli nie jest to bezwzględnie konieczne.
Zauważ, że w JUnit 4 inicjalizacja obiektu testowego ma miejsce tuż przed uruchomieniem testu, więc używanie inicjatorów pól jest bezpieczniejsze i zalecane.
źródło
W Twoim przypadku (tworzenie listy) nie ma różnicy w praktyce. Ale generalnie lepiej jest użyć setUp (), ponieważ pomoże to Junitowi poprawnie zgłosić wyjątki. Jeśli w konstruktorze / inicjatorze testu wystąpi wyjątek, oznacza to niepowodzenie testu . Jeśli jednak podczas konfiguracji wystąpi wyjątek, naturalne jest myślenie o tym jako o problemie podczas konfigurowania testu, a junit odpowiednio to zgłasza.
źródło
Preferuję najpierw czytelność, która najczęściej nie korzysta z metody konfiguracji. Robię wyjątek, gdy podstawowa operacja konfiguracji zajmuje dużo czasu i jest powtarzana w każdym teście.
W tym momencie przenoszę tę funkcjonalność do metody konfiguracji za pomocą
@BeforeClass
adnotacji (optymalizuję później).Przykład optymalizacji za pomocą
@BeforeClass
metody konfiguracji: używam dbunit do niektórych testów funkcjonalnych baz danych. Metoda konfiguracji odpowiada za wprowadzenie bazy danych w znany stan (bardzo wolny ... 30 sekund - 2 minuty w zależności od ilości danych). Ładuję te dane w metodzie konfiguracji z adnotacją,@BeforeClass
a następnie uruchamiam 10-20 testów dla tego samego zestawu danych, w przeciwieństwie do ponownego ładowania / inicjowania bazy danych w każdym teście.Korzystanie z Junit 3.8 (rozszerzenie TestCase, jak pokazano w przykładzie) wymaga napisania nieco więcej kodu niż tylko dodanie adnotacji, ale „uruchom raz przed konfiguracją klasy” jest nadal możliwe.
źródło
Ponieważ każdy test jest wykonywany niezależnie, z nową instancją obiektu, nie ma większego sensu, aby obiekt Test miał jakikolwiek stan wewnętrzny oprócz tego, który jest dzielony między
setUp()
i pojedynczym testem itearDown()
. To jeden z powodów (oprócz powodów podanych przez innych), dla których dobrze jest stosować tęsetUp()
metodę.Uwaga: to zły pomysł, aby obiekt testowy JUnit utrzymywał stan statyczny! Jeśli używasz zmiennej statycznej w swoich testach do celów innych niż śledzenie lub diagnostyka, unieważniasz część celu JUnit, który polega na tym, że testy mogą (lub mogą) być uruchamiane w dowolnej kolejności, każdy test uruchomiony z świeży, czysty stan.
Zaletą używania
setUp()
jest to, że nie musisz wycinać i wklejać kodu inicjującego w każdej metodzie testowej i że nie masz kodu konfiguracji testu w konstruktorze. W twoim przypadku różnica jest niewielka. Samo utworzenie pustej listy można bezpiecznie wykonać podczas jej wyświetlania lub w konstruktorze, ponieważ jest to trywialna inicjalizacja. Jednak, jak zauważyliście ty i inni, wszystko, co może wrzucić,Exception
powinno być zrobionesetUp()
, aby w razie niepowodzenia otrzymać zrzut stosu diagnostycznego.W twoim przypadku, gdy tworzysz tylko pustą listę, postąpiłbym tak samo, jak sugerujesz: Przypisz nową listę w miejscu deklaracji. Zwłaszcza, że w ten sposób masz możliwość zaznaczenia go,
final
jeśli ma to sens dla twojej klasy testowej.źródło
final
w pytaniu wspomniano o zaletach .Stałe wartości (używane w urządzeniach lub asercjach) powinny być inicjowane w ich deklaracjach i
final
(ponieważ nigdy się nie zmieniają)testowany obiekt powinien zostać zainicjowany w metodzie konfiguracji, ponieważ możemy ustawić coś na. Oczywiście możemy nie ustawić czegoś teraz, ale możemy to ustawić później. Utworzenie wystąpienia w metodzie init ułatwiłoby zmiany.
zależności testowanego obiektu, jeśli są one wyszydzane, nie powinny być nawet tworzone samodzielnie: obecnie makiety frameworki mogą utworzyć instancję poprzez odbicie.
Test bez zależności od makiety mógłby wyglądać następująco:
Test z zależnościami do wyizolowania mógłby wyglądać następująco:
źródło