web-dev-qa-db-fra.com

@BeforeClass et héritage - ordre d'exécution

J'ai une classe de base abstraite, que j'utilise comme base pour mes tests unitaires (TestNG 5.10). Dans cette classe, j'initialise l'environnement complet pour mes tests, la configuration de mappages de base de données, etc. Cette classe abstraite a une méthode avec une annotation @BeforeClass qui effectue l'initialisation.

Ensuite, j'étend cette classe avec des classes spécifiques dans lesquelles j'ai des méthodes @Test et également des méthodes @BeforeClass. Ces méthodes effectuent l’initialisation de l’environnement spécifique à la classe (par exemple, placer des enregistrements dans la base de données).

Comment puis-je appliquer un ordre spécifique des méthodes annotées @BeforeClass? J'ai besoin que ceux de la classe de base abstraite soient exécutés avant ceux de la classe d'extension.

Exemple:

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

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

    @Test
    doTests() {...}
}

Commande attendue:

A.doInitialization
B.doSpecificInitialization
B.doTests

Ordre actuel:

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

Ne mettez pas le @BeforeClass sur la classe abstract. Appelez-le de chaque sous-classe.

abstract class A {
    void doInitialization() {}
}

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

    @Test
    void doTests() {}
}

On dirait que TestNG a @BeforeClass(dependsOnMethods={"doInitialization"}) - l'essayer.

38
Bozho

edit: La réponse ci-dessous est pour JUnit, mais je vais le laisser ici de toute façon, car cela pourrait être utile.

Selon le JUnit api : "Les méthodes des super-classes @BeforeClass seront exécutées avant celles de la classe actuelle."

J'ai testé cela et cela semble fonctionner pour moi.

Toutefois, comme @Odys le mentionne ci-dessous, pour JUnit, les méthodes two doivent être nommées différemment. Sinon, seule la méthode sous-classe sera exécutée, car le parent sera ombré.

89
Fortega

J'ai ajouté public à la classe abstraite et TestNG (6.0.1) a exécuté doInitialization () avant doTests. TestNG n'exécute pas doInitialization() si je supprime public de la classe A.

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

class B extends A {    
 @Test
 doTests() {...}
}
6
Paul

Je viens d'essayer votre exemple avec 5.11 et j'obtiens d'abord le @BeforeClass de la classe de base.

Pouvez-vous poster votre fichier testng.xml? Peut-être que vous spécifiez à la fois A et B, alors que seul B est nécessaire.

N'hésitez pas à faire un suivi sur la liste de diffusion testng-users et nous pourrons examiner de plus près votre problème.

- Cédric

6
Cedric Beust

Je viens de traverser cela et j'ai trouvé un autre moyen d'y parvenir. Utilisez simplement alwaysRun sur @BeforeClass ou @BeforeMethod dans la classe abstraite, fonctionne comme prévu.

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

Pour JUnit: Comme @fortega l'a mentionné: Selon l'API JUnit: "Les méthodes des superclasses @BeforeClass seront exécutées avant celles de la classe actuelle."

Mais faites attention ne pas nommer les deux méthodes avec le même nom}. Puisque dans ce cas, la méthode parent sera masquée par le parent parent. La source .

2
Anatolii Stepaniuk

Quand je cours depuis: JUnitCore.runClasses (TestClass.class); Il exécutera correctement le parent avant l'enfant (Vous n'avez pas besoin de super.SetUpBeforeClass (); ) Si vous l'exécutez à partir d'Eclipse: exécutez la classe de base . Le travail autour de: Appelez explicitement la classe de base: ( BaseTest.setUpBeforeClass (); ) Vous souhaiterez peut-être avoir un indicateur dans la classe de base si vous l'exécutez à partir d'une application, afin de déterminer si elle est déjà configurée ou non. Ainsi, il ne s'exécute qu'une fois si vous l'exécutez via les deux méthodes possibles (par exemple, depuis Eclipse pour les tests personnels et via ANT pour une version de construction).

Cela semble être un bogue avec Eclipse, ou du moins des résultats inattendus.

2
Steve

Vérifiez votre déclaration d'importation . Elle devrait être

import org.testng.annotations.BeforeClass;

ne pas 

import org.junit.BeforeClass;

1
nishantrevo

dependsOnMethod peut être utilisé.

par exemple. en cas de printemps (AbstractTestNGSpringContextTests)

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

Pourquoi ne pas demander à votre méthode @BeforeClass d'appeler une méthode BeforeClass () spécifique vide qui peut ou non être écrasée par des sous-classes, comme ceci:

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

  protected void specificBeforeClass() {}
}

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

  // Tests
}
1
Tatome

Cela fonctionne pour moi -

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

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

       //do class specific init

    }   

    @Test
    doTests() {...}
}
0
srikar

Il existe une autre solution facile ici.

Ma situation particulière est que je dois injecter des services factices à partir de "BeforeClass" dans la sous-classe avant que "BeforeClass" dans la superclasse soit exécuté.

Pour ce faire, utilisez simplement un @ClassRule dans la sous-classe.

Par exemple:

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

J'espère que ça aide. Cela peut effectivement exécuter la configuration statique dans l'ordre "inverse".

0
vikingsteve

Pourquoi n'essayez-vous pas de créer une méthode abstraite doSpecialInit () dans votre super classe, appelée à partir de votre méthode annotée BeforeClass dans superclass.

Les développeurs qui héritent de votre classe doivent donc implémenter cette méthode.

0
Ludo

J'ai rencontré un problème similaire aujourd'hui, la seule différence était qu'une classe de base n'était pas abstraite

Voici mon cas

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

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

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

Il s'est avéré qu'une méthode @BeforeClass de la classe A n'a jamais été exécutée.

  • A.doInitialization () -> CECI N'A JAMAIS ÉTÉ EXÉCUTE silencieusement
  • B.doSpecificInitialization ()
  • B.doTests ()

En jouant avec les modificateurs de confidentialité, j'ai constaté que TestNG n'exécutera pas une méthode annotée @BeforeClass de la classe héritée si une méthode n'est pas visible depuis un héritier de la classe

Donc cela fonctionnera:

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

En conséquence, il se produit ce qui suit:

  • A.doInitialization ()
  • B.doSpecificInitialization ()
  • B.doTests ()
0
rodikno