2013-09-02 6 views
15

Ich habe eine Klasse, die ich als Basis für meine Komponententests verwenden. In dieser Klasse initialisiere ich die gesamte Umgebung für meine Tests, richte Datenbankzuordnungen ein, gebe mehrere Datensätze in mehrere Tabellen usw. ein. Diese Klasse hat eine Methode mit einer Annotation @BeforeClass, die die Initialisierung durchführt. Als nächstes erweitere ich diese Klasse um bestimmte Klassen, in denen ich @Test-Methoden verwende.JUNIT: run Setup nur einmal für eine große Anzahl von Testklassen

Meine Frage ist, da die Vorher-Klasse für alle diese Testklassen genau gleich ist, wie kann ich sicherstellen, dass sie nur einmal für alle Tests ausgeführt werden. Eine einfache Lösung ist, dass ich alle Tests in einer Klasse halten kann. Die Anzahl der Tests ist jedoch riesig, auch sie sind auf Basis von Funktionsköpfen kategorisiert. Sie befinden sich also in verschiedenen Klassen. Da sie jedoch das gleiche Setup benötigen, erben sie die @BeforeClass. Als Ergebnis wird das gesamte Setup mindestens einmal pro Testklasse durchgeführt, was viel mehr Zeit in Anspruch nimmt, als ich es vorziehen würde.

Ich könnte sie alle in verschiedene Unterpakete unter einem Paket, also, wenn es einen Weg gibt, wie ich einmal für alle Tests innerhalb dieses Pakets eingerichtet ausführen kann, wäre es toll.

+0

Haben Sie in etwa wie folgt untersucht: http://stackoverflow.com/questions/6580670/testsuite-setup-in-junit-4 – efan

+0

Bekannte Einschränkung. Wegen solcher Probleme bevorzuge ich persönlich [testng] (http://testng.org/doc/documentation-main.html) Framework. –

+0

@ G.Demecki Sie erwähnen das TestNG-Framework - warum haben Sie nicht eine Antwort mit dem TestNG-Weg erstellt, um dies für jeden zu tun, der sich fragen könnte? Die gleiche Stapelüberlauffrage für TestNG scheint nicht zu existieren. –

Antwort

5

JUnit dies nicht unterstützt, werden Sie die Standard-Java-Workarounds für Singletons verwenden müssen: Bewegen Sie den gemeinsamen Setup-Code in einen statischen Code-Block und dann eine leere Methode in dieser Klasse aufrufen:

static { 
    ...init code here... 
} 

public static void init() {} // Empty method to trigger the execution of the block above 

Stellen Sie sicher, dass alle Tests init() aufrufen, z. B. meine Eingabe in eine @BeforeClass Methode. Oder setzen Sie den statischen Codeblock in eine gemeinsame Basisklasse.

Alternativ eine globale Variable verwenden:

private static boolean initialize = true; 
public static void init() { 
    if(!initialize) return; 
    initialize = false; 

    ...init code here... 
} 
+1

Die Variable muss statisch sein, sonst wird sie überhaupt nicht funktionieren oder kompilieren ;-) Die Methode selbst statisch zu deklarieren ist nicht notwendig. Besonders wenn (wie in Tests, denen ich gerade begegnet bin) Sie Dinge wie @Inject oder andere DI-Mechanismen benötigen, ist es am besten, wenn Sie eine nicht-statische init() -Methode verwenden, die eine statische Hilfsvariable aufruft. –

+0

@WernerKeil Danke, behoben. –

0

Nicht sicher, ob noch immer JUnit verwendet wird und versucht wird, es zu reparieren, ohne Spring Runner zu verwenden (auch keine Federintegration). TestNG verfügt über diese Funktion. Aber hier ist eine JUnit-basierte Lösung.

Erstellen Sie eine RunOnce pro Thread-Operation wie folgt. Dies führt eine Liste von Klassen, für die die Operation ausgeführt wurde.

public class RunOnceOperation { 
private static final ThreadLocal t = new ThreadLocal(); 

public void run(Function f) { 
    if (t.get() == null) { 
     t.set(Arrays.asList(getClass())); 
     f.apply(0); 
    } else { 
     if (!((List) t.get()).contains(getClass())) { 
      ((List) t.get()).add(getClass()); 
      f.apply(0); 
     } 
    } 
    } 
} 

Zurück in Ihrem Gerät zu testen

@Before 
public beforeTest() { 
    operation.run(new Function<Integer, Void>() { 
     @Override 
     public Void apply(Integer t) { 
      checkBeanProperties(); 
      return null; 
     } 
    }); 
} 

private void checkBeanProperties() { 
    //I only want to check this once per class. 
    //Also my bean check needs instance of the class and can't be static. 
} 


My function interface is like this: 

interface Function<I,O> { 
O apply(I i); 
} 

Wenn Sie auf diese Weise verwenden, können Sie Operationen einmal pro Klasse mit Thread auszuführen.

+0

Raten Sie, dass Sie möglicherweise einige von java.util.function in Java 8 dafür wiederverwenden können. –

1

Erstellen Sie eine Basisklasse für alle Tests:

public class BaseTest { 
    static{ 
     /*** init code here ***/ 
    } 
} 

und jeder Test von ihm erben sollte:

public class SomeTest extends BaseTest { 

} 
12

Mit JUnit4 Testsuite Sie etwas tun können:

@RunWith(Suite.class) 
@Suite.SuiteClasses({ Test1IT.class, Test2IT.class }) 
public class IntegrationTestSuite 
{ 
    @BeforeClass 
    public static void setUp() 
    { 
     System.out.println("Runs before all tests in the annotation above."); 
    } 

    @AfterClass 
    public static void tearDown() 
    { 
     System.out.println("Runs after all tests in the annotation above."); 
    } 
} 

Dann führen Sie diese Klasse aus, wie Sie eine normale Testklasse ausführen würden, und es wird alle Ihre Tests ausführen.

0

Sie können eine BaseTest Klasse mit einer @BeforeClass Methode machen, dann müssen alle anderen Tests davon erben. Auf diese Weise wird, wenn jedes Testobjekt aufgebaut wird, @BeforeClass ausgeführt.

Vermeiden Sie auch, es nur einmal für alle Testsuite auszuführen, da alle Testfälle unabhängig sein sollten. @BeforeClass sollte nur einmal pro Testfall, nicht Test-Suite ausgeführt werden.

Verwandte Themen