Junit vor dem Unterricht (nicht statisch)

84

Gibt es bewährte Methoden, um Junit dazu zu bringen, eine Funktion einmal in einer Testdatei auszuführen, und sie sollte auch nicht statisch sein.

wie @BeforeClassbei nicht statischer Funktion?

Hier ist eine hässliche Lösung:

@Before void init(){
    if (init.get() == false){
        init.set(true);
        // do once block
    }
}

Nun, das ist etwas, was ich nicht tun möchte, und ich suche nach einer integrierten Junit-Lösung.

römisch
quelle
Nun, ich habe eine ziemlich große Hierarchie von Testdateien und Basistestdateien. Ich brauche die Möglichkeit, diese Aktion in den untergeordneten Testklassen zu überschreiben.
Roman
1
Ich hatte das gleiche Problem, bei dem nur der erste von vielen parametrisierten Tests eine Anmeldung durchführen sollte.
Dokaspar
5
Beachten Sie, dass die "hässliche" Lösung, die mit einfachem JUnit funktioniert, Reißtests nicht berücksichtigt.
Eskatos

Antworten:

21

Wenn Sie keine statischen Initialisierer für eine einmalige Initialisierung einrichten möchten und sich nicht speziell mit der Verwendung von JUnit befassen, schauen Sie sich TestNG an. TestNG unterstützt die nicht statische einmalige Initialisierung mit einer Vielzahl von Konfigurationsoptionen, die alle Anmerkungen verwenden.

In TestNG wäre dies gleichbedeutend mit:

@org.testng.annotations.BeforeClass
public void setUpOnce() {
   // One time initialization.
}

Zum Abreißen,

@org.testng.annotations.AfterClass
public void tearDownOnce() {
   // One time tear down.
}

Für das TestNG Äquivalent von JUnit 4 ist @Beforeund @Afterkönnen Sie verwenden @BeforeMethodund @AfterMethodjeweils.

Kartik
quelle
41

Eine einfache if-Anweisung scheint auch ziemlich gut zu funktionieren:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:test-context.xml"})
public class myTest {

    public static boolean dbInit = false;

    @Autowired
    DbUtils dbUtils;

    @Before
    public void setUp(){

        if(!dbInit){

            dbUtils.dropTables();
            dbUtils.createTables();
            dbInit = true;

        }
    }

 ...
Upgradingdave
quelle
1
Schön und einfach! Aber können Sie keine Möglichkeit finden, dies einfach anzupassen, um ein nicht statisches @AfterClassÄquivalent zu erhalten, das nach Ablauf aller Tests abreißt?
Steve Chambers
1
Sehen Sie hier für ein Update auf diese Methode , die für Testklassen funktionieren sollte , die Vererbung.
Steve Chambers
36

Die Verwendung eines leeren Konstruktors ist die einfachste Lösung. Sie können den Konstruktor in der erweiterten Klasse weiterhin überschreiben.

Aber es ist nicht optimal bei aller Vererbung. Aus diesem Grund verwendet JUnit 4 stattdessen Anmerkungen.

Eine andere Möglichkeit besteht darin, eine Hilfsmethode in einer Factory / Util-Klasse zu erstellen und diese Methode die Arbeit machen zu lassen.

Wenn Sie Spring verwenden, sollten Sie die @TestExecutionListenersAnmerkung in Betracht ziehen . So etwas wie dieser Test:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({CustomTestExecutionListener.class, 
     DependencyInjectionTestExecutionListener.class})
@ContextConfiguration("test-config.xml")
public class DemoTest {

Spring's AbstractTestExecutionListenerenthält zum Beispiel diese leere Methode, die Sie überschreiben können:

public void beforeTestClass(TestContext testContext) throws Exception {
    /* no-op */
}

HINWEIS:DependencyInjectionTestExecutionListener Beim Hinzufügen von benutzerdefinierten Elementen NICHT übersehen / übersehen TestExecutionListeners. Wenn Sie dies tun, werden alle Autodrähte sein null.

Espen
quelle
+1 Diese Technik löste mein Problem, als ich DbUnit verwenden und den Datensatz nur einmal pro Klasse laden wollte
Brad
+1 Dies ist perfekt ... für Leute, die nicht an eine alte Version des Frühlings gebunden sind. :(
Mike Miller
1
Wird dies beforeTestClass()vor oder nach der Initialisierung des Kontexts aufgerufen?
Dims
@ Dim nach Kontext initialisiert
Anand Rockzz
7

Verwenden Sie einfach @BeforeAllMethods/ @AfterAllMethodsannotations, um eine Methode im Instanzkontext (nicht statisch) auszuführen, in der alle injizierten Werte verfügbar sind.

Hierfür gibt es eine spezielle Testbibliothek:

https://mvnrepository.com/artifact/org.bitbucket.radistao.test/before-after-spring-test-runner/0.1.0

https://bitbucket.org/radistao/before-after-spring-test-runner/

Die einzige Einschränkung: funktioniert nur für Federprüfung.

(Ich bin der Entwickler dieser Testbibliothek)

Radistao
quelle
0

Ich habe es noch nie versucht, aber vielleicht können Sie einen Konstruktor ohne Argumente erstellen und von dort aus Ihre Funktion aufrufen?

römisch
quelle
Dies funktionieren würde, das Problem ist , dass ich die Möglichkeit brauchen , um diese Aktion in den Klassen außer Kraft zu setzen , die diese Basistestklasse erweitern
Roman
@ Roman: Oh, jetzt verstehe ich. Fügen Sie dies Ihrem Beitrag hinzu, dieser Kommentar macht die Dinge viel klarer.
Roman
Der Konstruktor wird so oft aufgerufen, wie die Testfälle vorhanden sind. Für jede Testmethode wird ein neues Testklassenobjekt erstellt. Die Verwendung eines Konstruktors ist hier also keine Lösung
manikanta
Dies funktioniert auch nicht mit der Abhängigkeitsinjektion, die auf dem bereits erstellten Objekt basiert.
Mike Miller
0

Der Artikel beschreibt 2 sehr schöne Lösungen für dieses Problem:

  1. "sauberer" Junit mit benutzerdefiniertem Runner (über die Schnittstelle, aber Sie können ihn mit einer benutzerdefinierten Anmerkung erweitern, z. B. @BeforeInstance)
  2. Spring Execution Listener, wie bereits von Espen erwähnt.
Kedzi
quelle
0

UPDATE: Bitte lesen Sie den Kommentar von Cherry, warum der folgende Vorschlag fehlerhaft ist. (Ich behalte die Antwort hier bei, anstatt sie zu löschen, da der Kommentar anderen nützliche Informationen darüber liefern kann, warum dies nicht funktioniert.)


Eine weitere Option, die bei Verwendung der Abhängigkeitsinjektion (z @PostConstruct. B. Spring) in Betracht gezogen werden sollte, ist . Dies garantiert, dass die Abhängigkeitsinjektion abgeschlossen ist, was bei einem Konstruktor nicht der Fall wäre:

@PostConstruct
public void init() {
    // One-time initialization...
}

Steve Chambers
quelle
6
Sehr schlechte Lösung bei Junit-Tests. Junit erstellt jedes Mal eine Testklasseninstanz, wenn eine Testmethode ausgeführt wird. Wenn es also 6 Testmethoden in der Klasse gibt, werden ein Klassenkonstruktor @Beforeund @AfterMethoden 6 Mal aufgerufen! In diesem Zusammenhang @PostConstructverhält es sich also wie eine @BeforeAnmerkung. Sie können es einfach testen: Fügen Sie einfach 2 Testmethoden in die Testklasse ein, fügen Sie hinzu @PostConstruct public void init() {System.out.println("started");}und sehen Sie in den Protokollen, wie oft es gedruckt wird.
Cherry
Zur Information bin ich gerade auf die JUnit-Dokumentation gestoßen , die bestätigt, was im obigen Kommentar über das Erstellen einer Instanz durch JUnit für jeden @TestLauf beschrieben wurde: "Um die Methode auszuführen, erstellt JUnit zuerst eine neue Instanz der Klasse und ruft dann die mit Anmerkungen versehene Methode auf."
Steve Chambers
-2

Verwenden Sie einfach @BeforeClass:

@BeforeClass
public static void init() {
}

Es ist nicht sinnvoll init, nicht statisch zu sein, da jeder Test in einer separaten Instanz ausgeführt wird. Die Instanz, auf der ausgeführt initwird, stimmt nicht mit der Instanz eines Tests überein.

Der einzige Grund, warum Sie möchten, dass es nicht statisch ist, besteht darin, es in Unterklassen zu überschreiben. Sie können dies jedoch auch mit statischen Methoden tun. Verwenden Sie einfach denselben Namen, und nur die Unterklassenmethode initwird aufgerufen.

fgb
quelle
1
Bei dieser ganzen Frage geht es um die Möglichkeit, dies nicht statisch zu tun. Dies ist erforderlich, wenn Sie einige Instanzvariablen für die Klasse benötigen.
Simon Forsberg
@ SimonForsberg Ja, und ich sage, die Frage ist ein XY-Problem. Die Operation sagte, das Problem habe das Verhalten in Kinderklassen außer Kraft gesetzt. Wenn das Beispiel Instanzvariablen benötigt, kann ich etwas anderes vorschlagen.
FGB
Siehe diesen Kommentar: stackoverflow.com/questions/2825615/…
Simon Forsberg
@ SimonForsberg Das ist der Kommentar, über den ich gesprochen habe. Was ist damit?
FGB