Prima e dopo l'esecuzione di Suite hook in jUnit 4.x


84

Sto cercando di preformare la configurazione e lo smontaggio per una serie di test di integrazione, utilizzando jUnit 4.4 per eseguire i test. Lo smontaggio deve essere eseguito in modo affidabile. Ho altri problemi con TestNG, quindi sto cercando di riportare su jUnit. Quali hook sono disponibili per l'esecuzione prima di eseguire qualsiasi test e dopo che tutti i test sono stati completati?

Nota: stiamo usando Maven 2 per la nostra build. Ho provato a usare le pre-& post-integration-testfasi di Maven , ma, se un test fallisce, Maven si ferma e non viene eseguito post-integration-test, il che non aiuta.


1
Per i test di integrazione dovresti usare il plug-in maven-fail-safe invece di infallibile. Questo non salterà post-integration-testse un test fallisce. Vedi anche questa pagina wiki .
Chris H.

puoi condividere la tua implementazione finale per favore?
vikramvi

Risposte:


114

Sì, è possibile eseguire in modo affidabile metodi di configurazione e smontaggio prima e dopo qualsiasi test in una suite di test. Fammi dimostrare in codice:

package com.test;

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

@RunWith(Suite.class)
@SuiteClasses({Test1.class, Test2.class})
public class TestSuite {

    @BeforeClass
    public static void setUp() {
        System.out.println("setting up");
    }

    @AfterClass
    public static void tearDown() {
        System.out.println("tearing down");
    }

}

Quindi la tua Test1classe sarebbe simile a:

package com.test;

import org.junit.Test;


public class Test1 {
    @Test
    public void test1() {
        System.out.println("test1");
    }

}

... e puoi immaginare che sia Test2simile. Se corri TestSuite, otterrai:

setting up
test1
test2
tearing down

Quindi puoi vedere che l'impostazione / lo smontaggio vengono eseguiti solo prima e dopo tutti i test, rispettivamente.

Il problema: funziona solo se stai eseguendo la suite di test e non stai eseguendo Test1 e Test2 come singoli test JUnit. Hai detto che stai usando Maven e il plugin Maven infallibile ama eseguire i test individualmente e non fa parte di una suite. In questo caso, consiglierei di creare una superclasse che ogni classe di test estende. La superclasse contiene quindi i metodi @BeforeClass e @AfterClass annotati. Sebbene non sia così pulito come il metodo sopra, penso che funzionerà per te.

Per quanto riguarda il problema con i test falliti, puoi impostare maven.test.error.ignore in modo che la compilazione continui sui test falliti. Questo non è raccomandato come pratica continua, ma dovrebbe farti funzionare finché tutti i tuoi test non saranno superati. Per maggiori dettagli, consulta la documentazione di Maven infallibile .


2
Questo ha funzionato perfettamente per me una volta che sono entrato nel plug-in maven-surefire e ho creato un elenco di inclusioni che indicava la suite che volevo eseguire.
Jherico

2
A partire da JUnit 4.8.2, questo non funziona bene con i test parametrizzati. I metodi @BeforeClass della Suite verranno eseguiti dopo il metodo @ Parameterized.Parameters del test, prevenendo qualsiasi dipendenza dalla configurazione della Suite.
Anm

In risposta a me stesso, quando si utilizza @Theories, la chiamata al metodo @DataPoints viene chiamata dopo @BeforeClass della Suite.
Anm

19
Ci scusiamo per il necro, ma l'aggiunta di BeforeClass / AfterClass a una super classe non funziona come previsto: vengono comunque chiamati dopo il completamento di ogni classe di test. Questo è per i posteri.
Subu Sankara Subramanian

3
È ancora un approccio valido? Come si evita la necessità di enumerare l'elenco delle classi di test nell'annotazione SuiteClasses?
Burhan Ali

34

Un mio collega ha suggerito quanto segue: puoi utilizzare un RunListener personalizzato e implementare il metodo testRunFinished (): http://junit.sourceforge.net/javadoc/org/junit/runner/notification/RunListener.html#testRunFinished(org. junit.runner.Result)

Per registrare RunListener basta configurare il plug-in sicuro come segue: http://maven.apache.org/surefire/maven-surefire-plugin/examples/junit.html sezione "Utilizzo di listener e reporter personalizzati"

Questa configurazione dovrebbe essere scelta anche dal plugin di sicurezza. Questa soluzione è ottima perché non è necessario specificare Suite, classi di test di ricerca o qualsiasi altra cosa: consente a Maven di fare la sua magia, aspettando che tutti i test finiscano.


5
+1 È la prima soluzione utilizzabile che ho visto senza l'ingombrante manutenzione di una classe Suites!
Stefan Haberl


6

Usando le annotazioni, puoi fare qualcosa del genere:

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

class SomethingUnitTest {
    @BeforeClass
    public static void runBeforeClass()
    {

    }

    @AfterClass
    public static void runAfterClass()
    {  

    }

    @Before  
    public void setUp()
    {

    }

    @After
    public void tearDown()
    {

    }

    @Test
    public void testSomethingOrOther()
    {

    }

}

4
La configurazione e lo smontaggio devono essere eseguiti una volta per corsa. Ciò sarebbe utile solo se tutti i test fossero in una classe.
sblundy

Questo imposta solo la suite di test individuale, non l'intera fase di test
Ben

3

Qui noi

  • aggiornato a JUnit 4.5,
  • ha scritto annotazioni per taggare ogni classe o metodo di test che necessitava di un servizio funzionante,
  • ha scritto gestori per ogni annotazione che conteneva metodi statici per implementare la configurazione e lo smontaggio del servizio,
  • esteso il solito Runner per individuare le annotazioni sui test, aggiungendo i metodi del gestore statico nella catena di esecuzione del test nei punti appropriati.

2

Per quanto riguarda "Nota: utilizziamo Maven 2 per la nostra build. Ho provato a utilizzare le fasi di test pre e post integrazione di Maven, ma, se un test fallisce, Maven si ferma e non esegue il test post-integrazione , il che non aiuta. "

puoi invece provare il plug-in fail-safe, penso che abbia la possibilità di garantire che la pulizia avvenga indipendentemente dall'installazione o dallo stato della fase intermedia


Sì, il plug-in fail-safe ti consentirà di specificare la configurazione e lo smontaggio specifici. Anche se non credo che esistesse un sistema di sicurezza al momento della pubblicazione di questa domanda.
Jason Axelson

2

A condizione che tutti i tuoi test possano estendere una classe "tecnica" e siano nello stesso pacchetto, puoi fare un piccolo trucco:

public class AbstractTest {
  private static int nbTests = listClassesIn(<package>).size();
  private static int curTest = 0;

  @BeforeClass
  public static void incCurTest() { curTest++; }

  @AfterClass
  public static void closeTestSuite() {
      if (curTest == nbTests) { /*cleaning*/ }             
  }
}

public class Test1 extends AbstractTest {
   @Test
   public void check() {}
}
public class Test2 extends AbstractTest {
   @Test
   public void check() {}
}

Tieni presente che questa soluzione presenta molti inconvenienti:

  • deve eseguire tutti i test del pacchetto
  • deve sottoclassare una classe "tecnica"
  • non è possibile utilizzare @BeforeClass e @AfterClass all'interno delle sottoclassi
  • se si esegue solo un test nel pacchetto, la pulizia non viene eseguita
  • ...

Per informazioni: listClassesIn () => Come trovi tutte le sottoclassi di una data classe in Java?


1
questo non è vero per quanto dimostrano i miei test. Ho una super classe che avvia un glassfish incorporato prima della lezione e lo spegne dopo la lezione. Ho quindi 2 classi che si estendono da quella super classe. La beforeclass viene eseguita prima di eseguire i test definiti in ciascuna classe.
Jonathan Morales Vélez

0

Per quanto ne so non esiste un meccanismo per farlo in JUnit, tuttavia potresti provare a creare sottoclassi Suite e sovrascrivere il metodo run () con una versione che fornisce hook.


0

Poiché maven-surefire-plugin non esegue prima la classe Suite, ma tratta la suite e le classi di test allo stesso modo, quindi possiamo configurare il plug-in come di seguito per abilitare solo le classi della suite e disabilitare tutti i test. Suite eseguirà tutti i test.

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.5</version>
            <configuration>
                <includes>
                    <include>**/*Suite.java</include>
                </includes>
                <excludes>
                    <exclude>**/*Test.java</exclude>
                    <exclude>**/*Tests.java</exclude>
                </excludes>
            </configuration>
        </plugin>

0

L'unico modo che penso quindi per ottenere la funzionalità che desideri sarebbe fare qualcosa di simile

import junit.framework.Test;  
import junit.framework.TestResult;  
import junit.framework.TestSuite;  

public class AllTests {  
    public static Test suite() {  
        TestSuite suite = new TestSuite("TestEverything");  
        //$JUnit-BEGIN$  
        suite.addTestSuite(TestOne.class);  
        suite.addTestSuite(TestTwo.class);  
        suite.addTestSuite(TestThree.class);  
        //$JUnit-END$  
     }  

     public static void main(String[] args)  
     {  
        AllTests test = new AllTests();  
        Test testCase = test.suite();  
        TestResult result = new TestResult();  
        setUp();  
        testCase.run(result);  
        tearDown();  
     }  
     public void setUp() {}  
     public void tearDown() {}  
} 

Uso qualcosa di simile in Eclipse, quindi non sono sicuro di quanto sia portatile al di fuori di quell'ambiente


3
Questo è un esempio per JUnit3 e l'OP ha chiesto JUnit4, ma nel caso in cui alcuni utenti di JUnit3 trovino questa domanda ... Per JUnit3, sarebbe meglio sbarazzarsi del metodo main () e avere il metodo suite () racchiudi TestSuite in una sottoclasse di junit.extensions.TestSetup. Hai ancora gli stessi avvertimenti dell'esempio di Julie sull'esecuzione delle singole classi di test nel tuo IDE.
NamshubWriter

0

Se non vuoi creare una suite e devi elencare tutte le tue classi di test, puoi usare la reflection per trovare il numero di classi di test dinamicamente e fare il conto alla rovescia in una classe base @AfterClass per eseguire il tearDown solo una volta:

public class BaseTestClass
{
    private static int testClassToRun = 0;

    // Counting the classes to run so that we can do the tear down only once
    static {
        try {
            Field field = ClassLoader.class.getDeclaredField("classes");
            field.setAccessible(true);

            @SuppressWarnings({ "unchecked", "rawtypes" })
            Vector<Class> classes = (Vector<Class>) field.get(BlockJUnit4ClassRunner.class.getClassLoader());
            for (Class<?> clazz : classes) {
                if (clazz.getName().endsWith("Test")) {
                    testClassToRun++;
                }
            }
        } catch (Exception ignore) {
        }
    }

    // Setup that needs to be done only once
    static {
        // one time set up
    }

    @AfterClass
    public static void baseTearDown() throws Exception
    {
        if (--testClassToRun == 0) {
            // one time clean up
        }
    }
}

Se preferisci usare @BeforeClass invece dei blocchi statici, puoi anche usare un flag booleano per fare il conteggio delle riflessioni e testare la configurazione solo una volta alla prima chiamata. Spero che questo aiuti qualcuno, mi ci è voluto un pomeriggio per trovare un modo migliore rispetto all'enumerazione di tutte le classi in una suite.

Ora tutto ciò che devi fare è estendere questa classe per tutte le tue classi di test. Avevamo già una classe base per fornire alcune cose comuni per tutti i nostri test, quindi questa era la soluzione migliore per noi.

L'ispirazione viene da questa risposta SO https://stackoverflow.com/a/37488620/5930242

Se non vuoi estendere questa classe ovunque, quest'ultima risposta SO potrebbe fare quello che vuoi.

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.