Hook für die Ausführung vor und nach der Suite in jUnit 4.x.

84

Ich versuche, Setup und Teardown für eine Reihe von Integrationstests durchzuführen, indem ich jUnit 4.4 verwende, um die Tests auszuführen. Der Abbau muss zuverlässig erfolgen. Ich habe andere Probleme mit TestNG, daher möchte ich wieder auf jUnit portieren. Welche Hooks können ausgeführt werden, bevor Tests ausgeführt werden und nachdem alle Tests abgeschlossen wurden?

Hinweis: Wir verwenden Maven 2 für unseren Build. Ich habe versucht, Maven's pre-& post-integration-testPhasen zu verwenden, aber wenn ein Test fehlschlägt, stoppt Maven und läuft nicht post-integration-test, was keine Hilfe ist.

sblundy
quelle
1
Für Integrationstests sollten Sie das Maven-Failsafe-Plugin anstelle von todsicher verwenden. Dies wird nicht übersprungen, post-integration-testwenn ein Test fehlschlägt. Siehe auch diese Wiki-Seite .
Chris H.
Können Sie uns bitte Ihre endgültige Implementierung mitteilen?
Vikramvi

Antworten:

113

Ja, es ist möglich, Einrichtungs- und Abbauverfahren vor und nach Tests in einer Testsuite zuverlässig auszuführen. Lassen Sie mich im Code demonstrieren:

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

}

Ihre Test1Klasse würde also ungefähr so ​​aussehen:

package com.test;

import org.junit.Test;


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

}

... und Sie können sich vorstellen, dass Test2das ähnlich aussieht. Wenn Sie rannten TestSuite, würden Sie bekommen:

setting up
test1
test2
tearing down

Sie sehen also, dass das Auf- und Abbauen nur vor bzw. nach allen Tests ausgeführt wird.

Der Haken: Dies funktioniert nur, wenn Sie die Testsuite ausführen und Test1 und Test2 nicht als einzelne JUnit-Tests ausführen. Sie haben erwähnt, dass Sie maven verwenden, und das todsichere Maven-Plugin führt Tests gerne einzeln und nicht als Teil einer Suite aus. In diesem Fall würde ich empfehlen, eine Oberklasse zu erstellen, die jede Testklasse erweitert. Die Oberklasse enthält dann die mit Anmerkungen versehenen Methoden @BeforeClass und @AfterClass. Obwohl nicht ganz so sauber wie die oben beschriebene Methode, denke ich, dass es für Sie funktionieren wird.

Für das Problem mit fehlgeschlagenen Tests können Sie maven.test.error.ignore so festlegen, dass der Build für fehlgeschlagene Tests fortgesetzt wird. Dies wird nicht als fortlaufende Übung empfohlen, sollte Sie jedoch funktionsfähig machen, bis alle Ihre Tests bestanden sind. Weitere Informationen finden Sie in der Dokumentation zu Maven Surefire .

Julie
quelle
2
Dies funktionierte perfekt für mich, als ich in das Maven-Surefire-Plugin ging und eine Includes-Liste erstellte, die auf die Suite zeigte, die ich ausführen wollte.
Jherico
1
Ab JUnit 4.8.2 funktioniert dies bei parametrisierten Tests nicht mehr gut. Die @ BeCoreClass-Methoden der Suite werden nach der @ Parameterized.Parameters-Methode des Tests ausgeführt, um jegliche Abhängigkeit vom Setup der Suite zu vermeiden.
Anm
Als Antwort auf mich selbst wird bei Verwendung von @Theories der Aufruf der @ DataPoints-Methode nach der @BeforeClass der Suite aufgerufen.
Anm
18
Entschuldigung für das Nekro - aber das Hinzufügen von BeforeClass / AfterClass zu einer Superklasse funktioniert nicht wie erwartet - sie werden nach Abschluss jeder Testklasse immer noch aufgerufen. Dies ist für die Nachwelt.
Subu Sankara Subramanian
3
Ist das noch ein gültiger Ansatz? Wie können Sie vermeiden, dass die Liste der Testklassen in der SuiteClasses-Annotation aufgelistet werden muss?
Burhan Ali
33

Ein Kollege von mir schlug Folgendes vor: Sie können einen benutzerdefinierten RunListener verwenden und die Methode testRunFinished () implementieren: http://junit.sourceforge.net/javadoc/org/junit/runner/notification/RunListener.html#testRunFinished(org. junit.runner.Result)

Um den RunListener zu registrieren, konfigurieren Sie einfach das todsichere Plugin wie folgt: http://maven.apache.org/surefire/maven-surefire-plugin/examples/junit.html Abschnitt "Verwenden von benutzerdefinierten Listenern und Reportern"

Diese Konfiguration sollte auch vom ausfallsicheren Plugin ausgewählt werden. Diese Lösung ist großartig, da Sie keine Suiten, Nachschlagetestklassen oder ähnliches angeben müssen. Maven kann damit seine Magie entfalten und darauf warten, dass alle Tests abgeschlossen sind.

Martin Vysny
quelle
5
+1 Das ist die erste brauchbare Lösung, die ich ohne die umständliche Wartung einer Suites-Klasse gesehen habe!
Stefan Haberl
7

Mit Anmerkungen können Sie Folgendes tun:

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

    }

}
Jroc
quelle
4
Das Setup & Teardown muss einmal pro Lauf ausgeführt werden. Dies würde nur helfen, wenn alle Tests in einer Klasse sind.
sblundy
3

Hier wir

  • Upgrade auf JUnit 4.5,
  • schrieb Anmerkungen, um jede Testklasse oder Methode zu kennzeichnen, die einen funktionierenden Dienst benötigte.
  • schrieb Handler für jede Annotation, die statische Methoden zum Implementieren des Auf- und Abbaus des Dienstes enthielten.
  • erweiterte den üblichen Runner, um die Anmerkungen zu Tests zu lokalisieren, und fügte die statischen Handlermethoden an den entsprechenden Stellen zur Testausführungskette hinzu.

quelle
2

Wie für "Hinweis: Wir verwenden Maven 2 für unseren Build. Ich habe versucht, die Testphasen vor und nach der Integration von Maven zu verwenden, aber wenn ein Test fehlschlägt, stoppt Maven und führt den Test nach der Integration nicht aus , was keine Hilfe ist. "

Sie können stattdessen das fehlersichere Plugin ausprobieren. Ich denke, es kann sicherstellen, dass die Bereinigung unabhängig vom Setup- oder Zwischenstufenstatus erfolgt

Binod Pant
quelle
Ja, mit dem ausfallsicheren Plugin können Sie bestimmte Einstellungen und Abbrüche festlegen. Obwohl ich nicht glaube, dass es zum Zeitpunkt der Veröffentlichung dieser Frage eine Ausfallsicherheit gab.
Jason Axelson
2

Vorausgesetzt, dass alle Ihre Tests eine "technische" Klasse erweitern und sich im selben Paket befinden, können Sie einen kleinen Trick machen:

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

Beachten Sie, dass diese Lösung viele Nachteile hat:

  • muss alle Tests des Pakets ausführen
  • muss eine "technische" Klasse unterordnen
  • Sie können @BeforeClass und @AfterClass nicht in Unterklassen verwenden
  • Wenn Sie nur einen Test in der Verpackung ausführen, wird die Reinigung nicht durchgeführt
  • ...

Zur Information: listClassesIn () => Wie finden Sie alle Unterklassen einer bestimmten Klasse in Java?

Christophe Blin
quelle
1
Dies ist nicht wahr, soweit meine eigenen Tests zeigen. Ich habe eine Superklasse, die einen eingebetteten Glasfisch vor der Klasse startet und ihn nach dem Unterricht abschaltet. Ich habe dann 2 Klassen, die von dieser Superklasse ausgehen. Die Vorklasse wird ausgeführt, bevor die in jeder Klasse definierten Tests ausgeführt werden.
Jonathan Morales Vélez
0

Soweit ich weiß, gibt es in JUnit keinen Mechanismus dafür. Sie können jedoch versuchen, Suite zu unterklassifizieren und die run () -Methode mit einer Version zu überschreiben, die Hooks bereitstellt.

user15299
quelle
0

Da das Maven-Surefire-Plugin nicht zuerst die Suite-Klasse ausführt, sondern Suite- und Testklassen gleich behandelt, können wir das Plugin wie folgt konfigurieren, um nur Suite-Klassen zu aktivieren und alle Tests zu deaktivieren. Suite führt alle Tests aus.

        <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>
Anurag Sharma
quelle
0

Der einzige Weg, um die gewünschte Funktionalität zu erhalten, wäre, so etwas zu tun

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

Ich verwende so etwas in Eclipse, daher bin ich mir nicht sicher, wie portabel es außerhalb dieser Umgebung ist

Jroc
quelle
3
Dies ist ein Beispiel für JUnit3, und das OP hat nach JUnit4 gefragt, aber nur für den Fall, dass einige JUnit3-Benutzer diese Frage finden ... Für JUnit3 ist es besser, die main () -Methode loszuwerden und die suite () -Methode zu verwenden Wickeln Sie die TestSuite in eine Unterklasse von junit.extensions.TestSetup. Sie haben immer noch die gleichen Einschränkungen wie Julies Beispiel für die Ausführung der einzelnen Testklassen in Ihrer IDE.
NamshubWriter
0

Wenn Sie keine Suite erstellen möchten und alle Ihre Testklassen auflisten müssen, können Sie mithilfe von Reflection die Anzahl der Testklassen dynamisch ermitteln und in einer Basisklasse @AfterClass herunterzählen, um den TearDown nur einmal auszuführen:

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

Wenn Sie lieber @BeforeClass anstelle der statischen Blöcke verwenden möchten, können Sie auch ein boolesches Flag verwenden, um die Reflexionszählung und das Test-Setup beim ersten Aufruf nur einmal durchzuführen. Ich hoffe, das hilft jemandem. Ich habe einen Nachmittag gebraucht, um einen besseren Weg zu finden, als alle Klassen in einer Suite aufzuzählen.

Jetzt müssen Sie diese Klasse nur noch für alle Ihre Testklassen erweitern. Wir hatten bereits eine Basisklasse, um einige gemeinsame Dinge für alle unsere Tests bereitzustellen, daher war dies die beste Lösung für uns.

Inspiration kommt von dieser SO-Antwort https://stackoverflow.com/a/37488620/5930242

Wenn Sie diese Klasse nicht überall erweitern möchten, kann diese letzte SO-Antwort das tun, was Sie wollen.

FredBoutin
quelle