it-swarm.it

Hook di esecuzione Prima e Dopo Suite in jUnit 4.x

Sto cercando di preformare l'installazione e lo smontaggio per una serie di test di integrazione, usando jUnit 4.4 per eseguire i test. Lo smontaggio deve essere eseguito in modo affidabile. Sto riscontrando altri problemi con TestNG, quindi sto cercando di tornare a jUnit. Quali hook sono disponibili per l'esecuzione prima che vengano eseguiti test e dopo che tutti i test sono stati completati?

Nota: stiamo usando Maven 2 per la nostra build. Ho provato ad usare pre- & post-integration-test fasi, ma, se un test fallisce, la maven si ferma e non viene eseguita post-integration-test, che non è di aiuto.

81
sblundy

Sì, è possibile eseguire in modo affidabile i metodi di installazione e smontaggio prima e dopo qualsiasi test in una suite di test. Lasciami dimostrare nel 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 classe Test1 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 Test2 sia simile. Se avessi eseguito TestSuite, otterrai:

setting up
test1
test2
tearing down

Quindi puoi vedere che il set up/tear down viene eseguito solo prima e dopo tutti i test, rispettivamente.

Il problema: funziona solo se si esegue la suite di test e non si eseguono Test1 e Test2 come singoli test JUnit. Hai detto che stai usando Maven, e al plugin surefire di Maven piace eseguire i test singolarmente e non parte di una suite. In questo caso, consiglierei di creare una superclasse estesa a ciascuna classe di test. 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 build continui sui test falliti. Questo non è raccomandato come pratica continua, ma dovrebbe farti funzionare fino al completamento di tutti i test. Per maggiori dettagli, consultare maven surefire documentazione .

111
Julie

Un mio collega mi ha suggerito quanto segue: è possibile 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 surefire come segue: http://maven.Apache.org/surefire/maven-surefire-plugin/examples/junit.html sezione "Utilizzo di listener e reporter personalizzati"

Questa configurazione dovrebbe essere selezionata anche dal plug-in fail-safe. Questa soluzione è eccezionale perché non è necessario specificare Suite, classi di test di ricerca o qualsiasi altra cosa: consente a Maven di fare la sua magia, in attesa che finiscano tutti i test.

32
Martin Vysny
11
Tobogganski

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()
    {

    }

}
7
Jroc

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 l'installazione e lo smontaggio del servizio,
  • ha esteso il solito Runner per individuare le annotazioni sui test, aggiungendo i metodi del gestore statico nella catena di esecuzione dei test nei punti appropriati.
3
carym

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 un solo test nel pacchetto, la pulizia non viene eseguita
  • ...

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

2
christophe blin

Per quanto riguarda "Nota: stiamo usando 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 arresta e non esegue test post-integrazione , che non aiuta. "

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

2
Binod Pant

Se non si desidera creare una suite e si devono elencare tutte le classi di test, è possibile utilizzare la riflessione per trovare dinamicamente il numero di classi di test e eseguire il conto alla rovescia in una classe di base @AfterClass per eseguire lo smontaggio una sola 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 si preferisce utilizzare @BeforeClass invece dei blocchi statici, è anche possibile utilizzare un flag booleano per eseguire il conteggio dei riflessi e testare l'installazione una sola volta alla prima chiamata. Spero che questo aiuti qualcuno, mi ci è voluto un pomeriggio per capire un modo migliore di elencare 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 è stata la soluzione migliore per noi.

L'ispirazione deriva da questo SO risposta https://stackoverflow.com/a/37488620/5930242

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

0
FredBoutin

Poiché maven-surefire-plugin non esegue prima la classe Suite ma tratta le stesse suite e test, quindi possiamo configurare il plug-in come di seguito per abilitare solo le classi 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
Anurag Sharma

L'unico modo in cui penso quindi di ottenere la funzionalità desiderata 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

0
Jroc

Per quanto ne so non esiste alcun meccanismo per farlo in JUnit, tuttavia è possibile provare a sottoclassare Suite e sovrascrivere il metodo run () con una versione che fornisce hook.

0
user15299