@BeforeClass und Vererbung - Ausführungsreihenfolge

90

Ich habe eine abstrakte Basisklasse, die ich als Basis für meine Unit-Tests verwende (TestNG 5.10). In dieser Klasse initialisiere ich die gesamte Umgebung für meine Tests, das Einrichten von Datenbankzuordnungen usw. Diese abstrakte Klasse verfügt über eine Methode mit einer @BeforeClassAnmerkung, die die Initialisierung durchführt.

Als nächstes erweitere ich diese Klasse um bestimmte Klassen, in denen ich @TestMethoden und auch @BeforeClassMethoden habe. Diese Methoden führen eine klassenspezifische Initialisierung der Umgebung durch (z. B. einige Datensätze in die Datenbank einfügen).

Wie kann ich eine bestimmte Reihenfolge der mit @BeforeClassAnmerkungen versehenen Methoden erzwingen ? Ich brauche diejenigen aus der abstrakten Basisklasse, die vor denen der erweiterten Klasse ausgeführt werden sollen.

Beispiel:

abstract class A {
    @BeforeClass
    doInitialization() {...}
}

class B extends A {
    @BeforeClass
    doSpecificInitialization() {...}

    @Test
    doTests() {...}
}

Erwartete Bestellung:

A.doInitialization
B.doSpecificInitialization
B.doTests

Aktuelle Bestellung:

B.doSpecificInitialization // <- crashes, as the base init is missing
(A.doInitialization        // <---not executed
 B.doTests)                // <-/
Dominik Sandjaja
quelle

Antworten:

50

Setzen Sie das nicht @BeforeClassauf die abstractKlasse. Nennen Sie es aus jeder Unterklasse.

abstract class A {
    void doInitialization() {}
}

class B extends A {
    @BeforeClass
    void doSpecificInitialization() {
        super.doInitialization();
    }

    @Test
    void doTests() {}
}

Scheint wie TestNG hat @BeforeClass(dependsOnMethods={"doInitialization"})- probieren Sie es aus.

Bozho
quelle
9
Das wollte ich eigentlich vermeiden: Keine Notwendigkeit, Methoden der super (abstrakten) Klasse explizit aufzurufen. Zumal ich auch Klassen habe, die von A erben, aber keine eigene @ BeforeClass-Methode haben. Ich müsste nur zu diesem Zweck eine einfügen.
Dominik Sandjaja
5
Die dependsOnMethodsProblemumgehung hat es geschafft. Obwohl ich einen "Superclass First" -Ansatz vorziehen würde ...
Dominik Sandjaja
1
Um "abhängige Methode" zu verwenden, sollte "doInitialization" nicht mit "@Test" kommentiert werden? Das ist ein Problem, da es technisch gesehen kein Test für sich ist ...
N3da
@BeforeClass sollte eine statische Methode kommentieren
Fabrizio Stellato
107

edit: Die Antwort unten ist für JUnit , aber ich werde sie trotzdem hier lassen, weil sie hilfreich sein könnte.

Laut der JUnit-API : "Die @ BeforeClass-Methoden von Oberklassen werden vor denen der aktuellen Klasse ausgeführt."

Ich habe das getestet und es scheint für mich zu funktionieren.

Wie @Odys weiter unten erwähnt, müssen für JUnit die beiden Methoden unterschiedlich benannt sein. Andernfalls wird nur die Unterklassenmethode ausgeführt, da das übergeordnete Verfahren schattiert wird.

Fortega
quelle
56
Obwohl die ursprüngliche Frage für TestNG war, bin ich hier angekommen, nachdem ich nach JUnit gegoogelt hatte, und Ihre Antwort hat geholfen - danke!
Teabot
9
für JUnit Sie müssen die beiden Methoden unterschiedlich sonst in Folge nur wird laufen die Unterklasse Methode wird jedoch als zu tun benannt haben , weil die Eltern werden beschattet wird.
Odys
2
@ Odys, vielen Dank, dass Sie dies erwähnt haben. Ich hatte Mühe herauszufinden, warum die "Setup" -Methode in meiner Unterklasse ausgeführt wurde, während die in der Oberklasse nicht ausgeführt wurde. Du hast mir gerade eine Menge Ärger erspart!
Tom Catullo
Du hast meinen Tag gerettet. Vielen Dank!
Raiks
7

Ich publichabe der abstrakten Klasse hinzugefügt und TestNG (6.0.1) hat zuvor die doInitialization () ausgeführt doTests. TestNG wird nicht ausgeführt, doInitialization()wenn ich publicaus Klasse A entferne .

public abstract class A {
 @BeforeClass
 doInitialization() {...}
}

class B extends A {    
 @Test
 doTests() {...}
}
Paul
quelle
1
Das ist wahr, aber irrelevant. Dies funktioniert nicht, wenn die Klasse B auch eine @BeforeClassMethode mit Annotation hat, wie im Fall des OP.
Jpaugh
1
Ich tat das gleiche. Es scheint, dass die Reihenfolge der Vererbung fehlt, wenn die Basismethode privat ist. Vielen Dank!
Manu
6

Ich habe gerade Ihr Beispiel mit 5.11 ausprobiert und bekomme zuerst die @BeforeClass der Basisklasse aufgerufen.

Können Sie Ihre Datei testng.xml veröffentlichen? Vielleicht geben Sie dort sowohl A als auch B an, während nur B erforderlich ist.

Fühlen Sie sich frei, die Mailingliste der Test-Benutzer zu verfolgen, und wir können Ihr Problem genauer untersuchen.

- Cedric

Cedric Beust
quelle
2
Keine .xml für testng definiert (explizit), es wird von Eclipse und Maven ausgeführt.
Dominik Sandjaja
Wie läuft es genau von Eclipse aus? Rechtsklick auf Klasse B?
Cedric Beust
4

Ich habe das gerade durchgesehen und einen weiteren Weg gefunden, dies zu erreichen. Verwenden Sie einfach alwaysRunauf @BeforeClassoder @BeforeMethodin der abstrakten Klasse, funktioniert wie erwartet.

public class AbstractTestClass {
    @BeforeClass(alwaysRun = true)
    public void generalBeforeClass() {
        // do stuff
        specificBeforeClass();
    }
}
Konrad Garus
quelle
2

Wenn ich laufe von: JUnitCore.runClasses (TestClass.class); Das übergeordnete Element wird vor dem untergeordneten Element ordnungsgemäß ausgeführt. (Sie benötigen super.SetUpBeforeClass () nicht. ) Wenn Sie es über Eclipse ausführen: Aus irgendeinem Grund kann die Basisklasse nicht ausgeführt werden. Die Problemumgehung : Rufen Sie die Basisklasse explizit auf: ( BaseTest.setUpBeforeClass (); ) Möglicherweise möchten Sie ein Flag in der Basisklasse haben, falls Sie es von einer Anwendung aus ausführen, um festzustellen, ob es bereits eingerichtet ist oder nicht. Es wird also nur einmal ausgeführt, wenn Sie es über beide möglichen Methoden ausführen (z. B. von Eclipse für persönliche Tests und über ANT für eine Build-Version).

Dies scheint ein Fehler mit Eclipse oder zumindest unerwartete Ergebnisse zu sein.

Steve
quelle
2

Für JUnit : Wie @fortega erwähnt hat: Laut JUnit-API: "Die @ BeforeClass-Methoden von Oberklassen werden vor denen der aktuellen Klasse ausgeführt."

Achten Sie jedoch darauf, nicht beide Methoden mit demselben Namen zu benennen . Da in diesem Fall die übergeordnete Methode vom untergeordneten übergeordneten Element ausgeblendet wird. Quelle .

Anatolii Stepaniuk
quelle
1

Wie wäre es, wenn Ihre @ BeforeClass-Methode eine leere spezifischeBeforeClass () -Methode aufruft, die möglicherweise von Unterklassen wie folgt überschrieben wird oder nicht:

public class AbstractTestClass {
  @BeforeClass
  public void generalBeforeClass() {
    // do stuff
    specificBeforeClass();
  }

  protected void specificBeforeClass() {}
}

public class SpecificTest {
  @Override
  protected void specificBeforeClass() {
    // Do specific stuff
  }

  // Tests
}
Tatome
quelle
3
BeforeClass muss statisch sein, damit Sie dies nicht mit
junit
1

dependsOnMethod kann verwendet werden.

zB im Falle von Spring ( AbstractTestNGSpringContextTests)

@BeforeClass(alwaysRun = true, dependsOnMethods = "springTestContextPrepareTestInstance")
Sandeep Jindal
quelle
1

Überprüfen Sie Ihre Importanweisung. Es sollte sein

import org.testng.annotations.BeforeClass;

nicht

import org.junit.BeforeClass;

nishantrevo
quelle
1

Das funktioniert bei mir -

abstract class A {
    @BeforeClass
    doInitialization() {...}
}

class B extends A {
    @Override
    @BeforeClass
    doInitialization() { 

       //do class specific init

    }   

    @Test
    doTests() {...}
}
Srikar
quelle
1
Fügen Sie eine kurze Erklärung hinzu, was dieser Code tut, und beantworten Sie die ursprüngliche Frage
Cray
0

Warum versuchen Sie nicht, eine abstrakte Methode doSpecialInit () in Ihrer Superklasse zu erstellen, die von Ihrer kommentierten BeforeClass-Methode in der Superklasse aufgerufen wird?

Entwickler, die Ihre Klasse erben, müssen diese Methode implementieren.

Ludo
quelle
Um ehrlich zu sein, hat sich vielleicht sogar die Logik in den letzten 3 1/2 Jahren geändert, seit ich diese Frage gestellt habe ... ;-) Also ja, vielleicht war das eine Idee, vielleicht hat es nicht funktioniert - ich ehrlich gesagt nicht merken.
Dominik Sandjaja
0

Hier gibt es eine andere einfache Lösung.

Meine besondere Situation ist, dass ich Mock-Services von "BeforeClass" in die Unterklasse einfügen muss, bevor "BeforeClass" in der Oberklasse ausgeführt wird.

Verwenden Sie dazu einfach ein @ClassRulein der Unterklasse.

Beispielsweise:

@ClassRule
public static ExternalResource mocksInjector = new ExternalResource() {
    @Override
    protected void before() {
        // inject my mock services here
        // Note: this is executed before the parent class @BeforeClass
    }
};

Ich hoffe das hilft. Dies kann das statische Setup effektiv in "umgekehrter" Reihenfolge ausführen.

vikingsteve
quelle
0

Ich hatte heute ein ähnliches Problem. Der einzige Unterschied war, dass eine Basisklasse nicht abstrakt war

Hier ist mein Fall

public class A {
    @BeforeClass
    private void doInitialization() {...}
}

public class B extends A {
    @BeforeClass
    private void doSpecificInitialization() {...}

    @Test
    public void doTests() {...}
}

Es kam vor, dass eine @BeforeClassMethode aus Klasse A nie ausgeführt wurde.

  • A.doInitialization () -> Dies wurde nie stillschweigend ausgeführt
  • B.doSpecificInitialization ()
  • B.doTests ()

Das Spiel mit der Privatsphäre Modifikatoren fand ich , dass TestNG nicht ausführen eine @BeforeClasskommentierte Methode von vererbten Klasse , wenn eine Methode nicht sichtbar von einer Klasse-Erbin ist

Das wird also funktionieren:

public class A {
    @BeforeClass
    private void doInitialization() {...}
}

public class B extends A {
    @BeforeClass
    //Here a privacy modifier matters -> please make sure your method is public or protected so it will be visible for ancestors
    protected void doSpecificInitialization() {...}

    @Test
    public void doTests() {...}
}

Infolgedessen passiert Folgendes:

  • A.doInitialization ()
  • B.doSpecificInitialization ()
  • B.doTests ()
rodikno
quelle
-1

In meinem Fall (JUnit) habe ich die gleichen Methoden wie setup () in der Basisklasse und der abgeleiteten Klasse. In diesem Fall wird nur die Methode der abgeleiteten Klasse aufgerufen, und ich habe sie die Basisklassenmethode aufrufen lassen.

Mike Sokolov
quelle
-2

Ein besserer und sauberer Weg, dies mithilfe der Vererbung zu erreichen, könnte folgender sein:

abstract class A {

    @BeforeClass
    void doInitialization() {}
}

class B extends A {

    @Override
    @BeforeClass
    void doInitialization() {
        super.doInitialization();
    }

    @Test
    void doTests() {}
}
kushal
quelle
1
Wenn die Methode in der Parent-Klasse zuerst ausgeführt werden muss, müssen Sie die Methode in der Child-Klasse nur anders als die der Parent-Klasse benennen (denn wenn sie dieselbe Signatur haben, wird der Polymorphismus aktiviert). Ich glaube, es ist ein sauberer Weg.
Anatolii Stepaniuk