Confusione di JUnit: utilizzare 'extends TestCase' o '@Test'?


152

Ho trovato molto confuso l'uso corretto (o almeno la documentazione) di JUnit. Questa domanda serve sia come riferimento futuro sia come domanda reale.

Se ho capito bene, ci sono due approcci principali per creare ed eseguire un test JUnit:

Approccio A (stile JUnit 3): creare una classe che estende TestCase e avviare i metodi di test con la parola test. Quando si esegue la classe come test JUnit (in Eclipse), tutti i metodi che iniziano con la parola testvengono eseguiti automaticamente.

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

Approccio B (stile JUnit 4): creare una classe "normale" e anteporre @Testun'annotazione al metodo. Si noti che NON è necessario avviare il metodo con la parola 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);
    }
}

Mescolare i due sembra non essere una buona idea, vedi ad esempio questa domanda di StackOverflow :

Ora, le mie domande:

  1. Qual è l'approccio preferito o quando useresti uno invece dell'altro?
  2. L'approccio B consente di verificare le eccezioni estendendo l'annotazione @Test come in @Test(expected = ArithmeticException.class). Ma come si verificano le eccezioni quando si utilizza l'approccio A?
  3. Quando si utilizza l'approccio A, è possibile raggruppare un numero di classi di test in una suite di test come questa:

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

    Ma questo non può essere usato con l'approccio B (poiché ogni testclass dovrebbe sottoclassare TestCase). Qual è il modo corretto di raggruppare i test per l'approccio B?

Modifica: ho aggiunto le versioni di JUnit ad entrambi gli approcci


Ho visto extends TestCasee poi ogni test anche annotato @Testsolo per confondere le cose. :)
EM-Creations,

Risposte:


119

La distinzione è piuttosto semplice:

  • l'estensione TestCaseè il modo in cui sono stati scritti i test unitari in JUnit 3 (ovviamente è ancora supportato in JUnit 4)
  • l'utilizzo @Testdell'annotazione è il modo introdotto da JUnit 4

Generalmente è necessario scegliere il percorso di annotazione, a meno che non sia necessaria la compatibilità con JUnit 3 (e / o una versione Java precedente a Java 5). Il nuovo modo ha diversi vantaggi:

Per verificare le eccezioni previste in una JUnit 3 TestCasedovresti rendere esplicito il testo.

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 ha introdotto un'altra modifica API, ma utilizza ancora le annotazioni. La nuova @Testannotazione è org.junit.jupiter.api.Test(la "vecchia" JUnit 4 era org.junit.Test), ma funziona praticamente come la JUnit 4.


Risposta utile e completa, ma non capisco perfettamente "controlla il messaggio di eccezione". Il controllo rispetto a una stringa codificata sarà un incubo per la manutenzione. Devi aver significato "controlla le proprietà del tuo tipo di eccezione specifico".
thSoft

3
@thSoft: non è spesso usato, ma a volte voglio assicurarmi che il metodo dell'eccezione menzioni il campo offensivo, per esempio. Quindi un semplice assertTrue(e.getMessage().contains("foo"))potrebbe essere utile.
Joachim Sauer,

1
Anche in JUnit4 questo è un linguaggio importante quando devi controllare il messaggio o qualche altra proprietà dell'eccezione (come la causa). Il expectedmetodo controlla solo il tipo.
Yishai,

@Yishai: è vero, ma la maggior parte delle volte sono già contento se il metodo genera il tipo corretto di Eccezione su input problematico.
Joachim Sauer,

Per tale motivo, JUnit 5 ha apportato una modifica sostanziale ai test delle eccezioni. assertThrows () è fantastico :-)
Marcus K. il

25

Ho una preferenza per JUnit 4 (approccio di annotazione) perché lo trovo più flessibile.

Se si desidera creare una suite di test in JUnit 4, è necessario creare una classe che raggruppa tutti i test in questo modo:

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 */
}

15

C'è una parte senza risposta alla tua domanda, ed è "Qual è il modo corretto di raggruppare i test per l'approccio B?"

La risposta ufficiale è che annoti una classe con un @RunWith (Suite.class) e poi usi l'annotazione @ Suite.SuiteClasses per elencare le classi. Ecco come lo fanno gli sviluppatori JUnit (elencando manualmente ogni classe in una suite). In molti modi questo approccio è un miglioramento, in quanto è banale e intuitivo aggiungere comportamenti prima della suite e dopo la suite (basta aggiungere un metodo @BeforeClass e @AfterClass alla classe annotata con @RunWith - molto meglio del vecchio TestFixture ).

Tuttavia, ha un passo indietro, in quanto le annotazioni non consentono di creare dinamicamente l'elenco di classi e aggirare il problema diventa un po 'brutto. Devi sottoclassare la classe Suite e creare dinamicamente la matrice di classi nella sottoclasse e passarla al costruttore Suite, ma questa è una soluzione incompleta in altre sottoclassi di Suite (come Categorie) che non funzionano con essa ed essenzialmente non supporta la raccolta dinamica delle classi di test.


1
+1 per questo. Dopo aver intrapreso un'attività per scrivere una soluzione dinamica per l'aggiunta di test a TestSuite, ho dovuto estendere TestCase in ciascuno dei miei test. Questo a sua volta ha interrotto i test delle unità precedentemente funzionanti che utilizzavano le annotazioni JUnit4 per definire le eccezioni previste. La mia ricerca di un modo per popolare dinamicamente una Test Suite mi ha portato a questo thread, e in particolare alla tua risposta, che credo sia uno dei pochi motivi desiderabili per continuare con JUnit 3.
8bitjunkie

4

Dovresti usare JUnit 4. È meglio.

Molti framework hanno iniziato a deprecare il supporto di JUnit 3.8.

Questo è tratto dalla documentazione di riferimento di Spring 3.0:

[Avvertenza] La gerarchia di legacy JUnit 3.8 è obsoleta

In generale, dovresti sempre provare a utilizzare l'ultima versione stabile di un framework quando inizi qualcosa di nuovo.


1
  1. L'approccio "preferito" sarebbe quello di utilizzare le annotazioni introdotte dal 4 giugno. Facilitano molte cose (vedi la tua seconda domanda)

  2. Per questo puoi usare un semplice blocco try / catch:


public void testForException() {
    try {
        Integer.parseInt("just a string");
        fail("Exception should have been thrown");
    } catch (final Exception e) {
        // expected
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.