Zamieszanie JUnit: użyj „extends TestCase” lub „@Test”?

152

Uważam, że właściwe użycie (lub przynajmniej dokumentacja) JUnit jest bardzo zagmatwane. To pytanie służy zarówno jako odniesienie w przyszłości, jak i prawdziwe pytanie.

Jeśli dobrze zrozumiałem, istnieją dwa główne podejścia do tworzenia i uruchamiania testu JUnit:

Podejście A (styl JUnit 3): utwórz klasę rozszerzającą TestCase i rozpocznij metody testowe słowem test. Uruchamiając klasę jako test JUnit (w Eclipse), wszystkie metody zaczynające się od słowa testsą automatycznie uruchamiane.

import junit.framework.TestCase;

public class DummyTestA extends TestCase {

    public void testSum() {
        int a = 5;
        int b = 10;
        int result = a + b;
        assertEquals(15, result);
    }
}

Podejście B (styl JUnit 4): utwórz „normalną” klasę i dołącz @Testadnotację do metody. Zauważ, że NIE musisz rozpoczynać metody słowem test.

import org.junit.*;
import static org.junit.Assert.*;

public class DummyTestB {

    @Test
    public void Sum() {
        int a = 5;
        int b = 10;
        int result = a + b;
        assertEquals(15, result);
    }
}

Mieszanie tych dwóch nie wydaje się być dobrym pomysłem, zobacz na przykład to pytanie dotyczące stackoverflow :

Teraz moje pytania:

  1. Jakie jest preferowane podejście lub kiedy użyjesz jednego zamiast drugiego?
  2. Podejście B umożliwia testowanie wyjątków poprzez rozszerzenie adnotacji @Test, jak w @Test(expected = ArithmeticException.class). Ale jak testować wyjątki, używając podejścia A?
  3. Korzystając z podejścia A, możesz zgrupować kilka klas testowych w zestawie testów w następujący sposób:

    TestSuite suite = new TestSuite("All tests");
    suite.addTestSuite(DummyTestA.class);
    suite.addTestSuite(DummyTestAbis.class);

    Ale nie można tego użyć z podejściem B (ponieważ każda klasa testowa powinna mieć podklasę TestCase). Jaki jest właściwy sposób grupowania testów dla podejścia B?

Edycja: Dodałem wersje JUnit do obu podejść

Rabarberskiego
źródło
Widziałem, extends TestCasea następnie każdy test został również opatrzony adnotacją, @Testaby zmylić rzeczy. :)
EM-Creations

Odpowiedzi:

119

Rozróżnienie jest dość łatwe:

  • rozszerzenie TestCaseto sposób, w jaki testy jednostkowe zostały napisane w JUnit 3 (oczywiście nadal jest obsługiwane w JUnit 4)
  • użycie @Testadnotacji jest sposobem wprowadzonym przez JUnit 4

Generalnie należy wybrać ścieżkę adnotacji, chyba że wymagana jest zgodność z JUnit 3 (i / lub wersją Java starszą niż Java 5). Nowy sposób ma kilka zalet:

Aby przetestować oczekiwane wyjątki w JUnit 3 TestCase, musiałbyś zamieścić tekst w wyraźny sposób.

public void testMyException() {
  try {
    objectUnderTest.myMethod(EVIL_ARGUMENT);
    fail("myMethod did not throw an Exception!");
  } catch (MyException e) {
    // ok!
    // check for properties of exception here, if desired
  }
}

JUnit 5 wprowadził kolejną zmianę interfejsu API, ale nadal używa adnotacji. Nowa @Testadnotacja to org.junit.jupiter.api.Test(była to „stara” JUnit 4 org.junit.Test), ale działa prawie tak samo jak JUnit 4.

Joachim Sauer
źródło
Pomocna i dokładna odpowiedź, ale nie do końca rozumiem „sprawdź komunikat o wyjątku”. Sprawdzanie na sztywno zakodowany ciąg będzie koszmarem konserwacji. Musiałeś mieć na myśli „sprawdzenie właściwości konkretnego typu wyjątku”.
thSoft
3
@thSoft: nie jest często używany, ale czasami chcę się upewnić, że metoda wyjątku wspomina na przykład o naruszającym polu. Wtedy prosty assertTrue(e.getMessage().contains("foo"))może być przydatny.
Joachim Sauer,
1
Nawet w JUnit4 jest to ważny idiom, gdy musisz sprawdzić wiadomość lub inną właściwość wyjątku (na przykład przyczynę). expectedJedyna metoda sprawdza typu.
Yishai,
@Yishai: to prawda, ale w większości przypadków jestem już zadowolony, jeśli metoda rzuca prawidłowy typ wyjątku na problematyczne dane wejściowe.
Joachim Sauer
Z tego powodu JUnit 5 dokonał przełomowej zmiany w testowaniu wyjątków. assertThrows () jest fantastyczny :-)
Marcus K.
25

Preferuję JUnit 4 (podejście adnotacyjne), ponieważ uważam, że jest bardziej elastyczny.

Jeśli chcesz zbudować zestaw testów w JUnit 4, musisz utworzyć klasę grupującą wszystkie testy w następujący sposób:

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;


@RunWith(Suite.class)
@SuiteClasses({
    Test1.class,
    Test2.class,
    Test3.class,
    Test4.class
})public class TestSuite
{
 /* empty class */
}
Grimmy
źródło
15

Na Twoje pytanie pozostaje część bez odpowiedzi, a mianowicie: „Jaki jest właściwy sposób grupowania testów dla podejścia B?”

Oficjalna odpowiedź jest taka, że ​​dodajesz adnotację do klasy @RunWith (Suite.class), a następnie używasz adnotacji @ Suite.SuiteClasses, aby wyświetlić listę klas. W ten sposób robią to programiści JUnit (ręcznie wyświetlają każdą klasę w pakiecie). Pod wieloma względami to podejście jest ulepszeniem, ponieważ dodawanie zachowań przed i po pakiecie jest trywialne i intuicyjne (wystarczy dodać metodę @BeforeClass i @AfterClass do klasy z adnotacją @RunWith - znacznie lepiej niż stary TestFixture ).

Ma jednak krok wstecz, ponieważ adnotacje nie pozwalają na dynamiczne tworzenie listy klas, a obejście tego problemu staje się nieco brzydkie. Musisz utworzyć podklasę klasy Suite i dynamicznie utworzyć tablicę klas w podklasie i przekazać ją do konstruktora Suite, ale jest to niekompletne rozwiązanie, ponieważ inne podklasy Suite (takie jak kategorie) nie działają z nią i zasadniczo nie obsługują dynamicznej kolekcji klas testowych.

Yishai
źródło
1
+1 za to. Po rozpoczęciu zadania polegającego na napisaniu dynamicznego rozwiązania dodawania testów do pakietu TestSuite, musiałem rozszerzyć TestCase w każdym z moich testów. To z kolei zepsuło działające wcześniej testy jednostek, które wykorzystywały adnotacje JUnit4 do definiowania oczekiwanych wyjątków. Moje poszukiwania sposobu na dynamiczne wypełnianie pakietu testowego doprowadziły mnie do tego wątku, a konkretnie do Twojej odpowiedzi, która moim zdaniem jest jednym z niewielu pozostałych pożądanych powodów, aby kontynuować pracę z JUnit 3.
8bitjunkie
4

Powinieneś użyć JUnit 4. Jest lepiej.

Wiele frameworków zaczęło wycofywać obsługę JUnit 3.8.

To pochodzi z dokumentacji referencyjnej Spring 3.0:

[Ostrzeżenie] Starsza hierarchia klas JUnit 3.8 jest przestarzała

Generalnie, kiedy zaczynasz coś nowego, powinieneś zawsze próbować używać najnowszej stabilnej wersji frameworka.

Espen
źródło
1
  1. „Preferowanym” podejściem byłoby użycie adnotacji, które zostały wprowadzone od czerwca 4. Ułatwiają one wiele rzeczy (zobacz drugie pytanie)

  2. Możesz użyć do tego prostego bloku try / catch:


public void testForException() {
    try {
        Integer.parseInt("just a string");
        fail("Exception should have been thrown");
    } catch (final Exception e) {
        // expected
    }
}
czarny666
źródło