Junit - raz uruchom metodę konfiguracji


Odpowiedzi:


205

Chociaż zgadzam się z @assylias, używanie @BeforeClassjest klasycznym rozwiązaniem nie zawsze jest wygodne. Metoda z adnotacją @BeforeClassmusi być statyczna. Jest to bardzo niewygodne w przypadku niektórych testów, które wymagają wystąpienia przypadku testowego. Na przykład testy oparte na Spring, które służą @Autowireddo pracy z usługami zdefiniowanymi w kontekście Spring.

W tym przypadku osobiście używam zwykłej setUp()metody z @Beforeadnotacją i zarządzam własną flagą static(!) boolean:

private static boolean setUpIsDone = false;
.....
@Before
public void setUp() {
    if (setUpIsDone) {
        return;
    }
    // do the setup
    setUpIsDone = true;
}

10
Dodając do komentarza Kenny'ego Casona, dlaczego musi być statyczny. Musi być statyczny, ponieważ JUnit tworzy nową instancję klasy testowej dla każdej metody @Test. Zmienna instancji zostanie zresetowana do wartości domyślnej (false) dla każdej instancji, jeśli nie jest statyczna. Zobacz więcej informacji: martinfowler.com/bliki/JunitNewInstance.html
dustin.schultz

2
Działa to z wyjątkiem przypadku, gdy setUp()metoda znajduje się w nadklasie - opublikowali odpowiedź poniżej, próbując rozwiązać ten problem.
Steve Chambers

4
Waham się, czy powiedzieć to komuś, kto ma 84 tys. Przedstawicieli, ale BeforeClass w rzeczywistości nie odpowiada na pytanie: BeforeClass jest uruchamiany na początku każdej klasy testowej. Ale OP poprosił o taki, który uruchamia się „tylko raz przed wszystkimi testami”. Proponowane przez Ciebie rozwiązanie mogłoby to zrobić, ale musiałbyś sprawić, by wszystkie Twoje klasy testowe rozszerzyły klasę „CommonTest” ...
mike gryzoń

1
@mikerodent, IMHO OP zapytał o wszystkie testy w jego przypadku testowym, a nie wszystkie testy ogólnie. Więc twój komentarz jest mniej istotny. Swoją drogą, nie martw się, że powiesz cokolwiek komukolwiek, nawet jeśli ma dobrą reputację. Tak przynajmniej robię :). Moja reputacja była znacznie niższa w sierpniu 2012 r., Kiedy odpowiedziałem na pytanie.
AlexR

Nie działa w moim przypadku, zmienne zainicjowane w konfiguracji są resetowane po każdym teście, więc nie ma sensu inicjować go tylko raz.
Aphax

89

Możesz użyć z BeforeClassadnotacji :

@BeforeClass
public static void setUpClass() {
    //executed only once, before the first test
}

12
Nie mogę tego użyć, mam kilka metod konfiguracji, które są oparte na niestatycznych składnikach, takich jak getClass ()
Bober02

1
@ Bober02 BeforeClass musi być rzeczywiście statyczny. Jeśli nie możesz tego użyć, druga odpowiedź zapewnia obejście.
assylias

2
Na pewno nie możesz użyć TheClassYouWant.classzamiast wywołania getClass ()? Jest to rzeczywista Java: String.class.getName().
stolsvik


1
@mikerodent Zrozumiałem to pytanie jako „wszystkie testy w klasie” - ale masz rację, może nie być to, czego chciał OP.
assylias

29

JUnit 5 ma teraz adnotację @BeforeAll:

Wskazuje, że metoda z adnotacjami powinna zostać wykonana przed wszystkimi metodami @Test w bieżącej klasie lub hierarchii klas; analogicznie do @BeforeClass w JUnit 4. Takie metody muszą być statyczne.

Wydaje się, że adnotacje dotyczące cyklu życia JUnit 5 w końcu się udało! Możesz odgadnąć, które adnotacje są dostępne, nawet nie patrząc (np. @BeforeEach @AfterAll)


6
Ma ten sam problem @BeforeClass, co musi static. Rozwiązanie IMO @ AlexR jest ładniejsze.
zengr

@zengr zwykle się z tobą zgadza: jak powiedziałem AlexR, jego rozwiązanie wymaga, aby wszystkie klasy testowe były podklasami z klasy CommonTest, jeśli ma to być uruchomione tylko raz. Ale jest to proste, tak proste, jak to tylko możliwe, i IMHO prawdopodobnie nie powinieneś używać "fantazyjnego" rozwiązania dostarczanego przez framework, gdy prosty mechanizm jest dostępny z języka. Chyba że ma ku temu dobry powód. Ponadto użycie prostej rzeczy, takiej jak jego, z dobrą nazwą typu „robi to, co mówi na puszce”, pomaga w czytelności.
mike gryzoń

Powiedziawszy to, znowu IMHO, wydaje się znacznie więcej uzasadnienia dla posiadania adnotacji „AfterAll”: byłoby bardzo trudne i wymyślone opracowanie mechanizmu wykrywania, kiedy wszystkie testy zostały wykonane. I odwrotnie, oczywiście, puryści prawdopodobnie powiedzą, że nigdy nie powinno się wykonywać „ostatecznego czyszczenia”, tj. Że każde „tearDown” powinno pozostawić wszystkie zasoby w nienaruszonym stanie… i prawdopodobnie mają rację!
mike gryzoń

Czy to działa z Maven, gdzie jest wiele modułów, każdy z własnymi testami?
Mark Boon

@mike gryzonie, w moim przypadku konfigurowanie i niszczenie plików testowych w systemie plików przed / po każdym teście wydaje się prowadzić do zakleszczenia plików. Na razie dotarłem samodzielnie do rozwiązania AlexR do konfiguracji raz. Mam już dwie statyczne flagi, ustawione i brudne. setup () wywołuje cleanup (), jeśli początkowo zostanie wykryty stan brudny lub jeśli błąd konfiguracji prowadzi do stanu brudnego. Aby posprzątać po uruchomieniu testów, uruchamiam je ponownie. Niechlujny, wcale nie idealny, nie w naszym procesie tworzenia. Wciąż szukam lepszego sposobu (jUnit 4.12).
Rebeccah

9

Gdy setUp()jest w nadklasie klasy testowej (np. AbstractTestBasePoniżej), zaakceptowaną odpowiedź można zmodyfikować w następujący sposób:

public abstract class AbstractTestBase {
    private static Class<? extends AbstractTestBase> testClass;
    .....
    public void setUp() {
        if (this.getClass().equals(testClass)) {
            return;
        }

        // do the setup - once per concrete test class
        .....
        testClass = this.getClass();
    }
}

Powinno to zadziałać w przypadku pojedynczej setUp()metody niestatycznej , ale nie jestem w stanie stworzyć odpowiednika tearDown()bez zagłębiania się w świat złożonej refleksji… Nagrody dla każdego, kto może!


3

Edytować: właśnie dowiedziałem się podczas debugowania, że ​​instancja klasy jest również tworzona przed każdym testem. Myślę, że adnotacja @BeforeClass jest tutaj najlepsza.

Możesz też ustawić na konstruktorze, klasa testowa jest przecież klasą. Nie jestem pewien, czy to zła praktyka, ponieważ prawie wszystkie inne metody są opatrzone adnotacjami, ale działa. Możesz stworzyć takiego konstruktora:

public UT () {
    // initialize once here
}
@Test
// Some test here...

Ctor zostanie wywołany przed testami, ponieważ nie są one statyczne.



0

Moje brudne rozwiązanie to:

public class TestCaseExtended extends TestCase {

    private boolean isInitialized = false;
    private int serId;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        if(!isInitialized) {
            loadSaveNewSerId();
            emptyTestResultsDirectory();
            isInitialized = true;
        }
    }

   ...

}

Używam go jako podstawy do wszystkich moich przypadków testowych.


klasa publiczna TestCaseExtended rozszerza TestCase {private static boolean isInitialized = false; prywatny statyczny TestCaseExtended caseExtended; private int serId; @Override public void setUp () zgłasza wyjątek {super.setUp (); if (! isInitialized) {caseExtended = new TestCaseExtended (); caseExtended.loadSaveNewSerId (); caseExtended.emptyTestResultsDirectory (); isInitialized = true; }}
Obi Two

0

Jeśli nie chcesz wymuszać deklaracji zmiennej, która jest ustawiana i sprawdzana w każdym podteście, dodanie tego do SuperTestu może zrobić:

public abstract class SuperTest {

    private static final ConcurrentHashMap<Class, Boolean> INITIALIZED = new ConcurrentHashMap<>();
    protected final boolean initialized() {
        final boolean[] absent = {false};
        INITIALIZED.computeIfAbsent(this.getClass(), (klass)-> {
            return absent[0] = true;
        });
        return !absent[0];
    }
}



public class SubTest extends SuperTest {
    @Before
    public void before() {
        if ( super.initialized() ) return;

         ... magic ... 
    }

}

0

Rozwiązałem ten problem w ten sposób:

Dodaj do swojej klasy abstrakcyjnej Base (mam na myśli klasę abstrakcyjną, w której inicjalizujesz sterownik w metodzie setUpDriver () ) tę część kodu:

private static boolean started = false;
static{
    if (!started) {
        started = true;
        try {
            setUpDriver();  //method where you initialize your driver
        } catch (MalformedURLException e) {
        }
    }
}

A teraz, jeśli twoje klasy testowe będą rozszerzać się z klasy abstrakcyjnej Base -> metoda setUpDriver () zostanie wykonana przed pierwszym @Test tylko JEDEN raz na uruchomienie.


0

Użyj metody @PostConstruct Springa, aby wykonać całą pracę inicjalizacyjną, a ta metoda jest uruchamiana przed wykonaniem któregokolwiek z @Test

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.