web-dev-qa-db-fra.com

Crochet d'exécution avant et après la suite dans jUnit 4.x

J'essaie de pré-configurer et démonter un ensemble de tests d'intégration, en utilisant jUnit 4.4 pour exécuter les tests. Le démontage doit être exécuté de manière fiable. J'ai d'autres problèmes avec TestNG, donc je cherche à porter sur jUnit. Quels crochets sont disponibles pour exécution avant l'exécution des tests et une fois tous les tests terminés?

Remarque: nous utilisons maven 2 pour notre build. J'ai essayé d'utiliser le pre- & post-integration-test phases, mais, si un test échoue, maven s'arrête et ne s'exécute pas post-integration-test, ce qui n'est d'aucune aide.

81
sblundy

Oui, il est possible d'exécuter de manière fiable les méthodes de configuration et de démontage avant et après tout test dans une suite de tests. Permettez-moi de démontrer dans le code:

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

}

Ainsi, votre classe Test1 ressemblerait à quelque chose comme:

package com.test;

import org.junit.Test;


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

}

... et vous pouvez imaginer que Test2 se ressemble. Si vous exécutiez TestSuite, vous obtiendrez:

setting up
test1
test2
tearing down

Vous pouvez donc voir que la configuration/démontage ne s'exécute qu'avant et après tous les tests, respectivement.

Le hic: cela ne fonctionne que si vous exécutez la suite de tests et que vous n'exécutez pas Test1 et Test2 en tant que tests JUnit individuels. Vous avez mentionné que vous utilisez maven, et le plugin maven surefire aime exécuter des tests individuellement, et ne fait pas partie d'une suite. Dans ce cas, je recommanderais de créer une superclasse que chaque classe de test étend. La superclasse contient ensuite les méthodes annotées @BeforeClass et @AfterClass. Bien que pas aussi propre que la méthode ci-dessus, je pense que cela fonctionnera pour vous.

En ce qui concerne le problème des tests ayant échoué, vous pouvez définir maven.test.error.ignore afin que la construction se poursuive sur les tests ayant échoué. Ce n'est pas recommandé comme pratique continue, mais cela devrait vous permettre de fonctionner jusqu'à ce que tous vos tests réussissent. Pour plus de détails, consultez la documentation de maven surefire .

111
Julie

Un de mes collègues a suggéré ce qui suit: vous pouvez utiliser un RunListener personnalisé et implémenter la méthode testRunFinished (): http://junit.sourceforge.net/javadoc/org/junit/runner/notification/RunListener.html# testRunFinished (org.junit.runner.Result)

Pour enregistrer RunListener, il suffit de configurer le plugin surefire comme suit: http://maven.Apache.org/surefire/maven-surefire-plugin/examples/junit.html section "Utilisation d'écouteurs et de reporters personnalisés"

Cette configuration doit également être choisie par le plug-in à sécurité intégrée. Cette solution est excellente car vous n'avez pas à spécifier de suites, de classes de test de recherche ou de tout cela - elle permet à Maven de faire sa magie, en attendant que tous les tests se terminent.

32
Martin Vysny
11
Tobogganski

En utilisant des annotations, vous pouvez faire quelque chose comme ceci:

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

    }

}
7
Jroc

Ici, nous

  • mis à niveau vers JUnit 4.5,
  • écrit des annotations pour marquer chaque classe ou méthode de test qui avait besoin d'un service fonctionnel,
  • écrit des gestionnaires pour chaque annotation contenant des méthodes statiques pour implémenter la configuration et le démontage du service,
  • étendu le Runner habituel pour localiser les annotations sur les tests, en ajoutant les méthodes du gestionnaire statique dans la chaîne d'exécution des tests aux points appropriés.
3
carym

À condition que tous vos tests puissent étendre une classe "technique" et soient dans le même package, vous pouvez faire une petite astuce:

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

Sachez que cette solution présente de nombreux inconvénients:

  • doit exécuter tous les tests du package
  • doit sous-classer une classe "technique"
  • vous ne pouvez pas utiliser @BeforeClass et @AfterClass à l'intérieur des sous-classes
  • si vous exécutez un seul test dans le package, le nettoyage n'est pas effectué
  • ...

Pour information: listClassesIn () => Comment trouvez-vous toutes les sous-classes d'une classe donnée en Java?

2
christophe blin

Quant à "Remarque: nous utilisons maven 2 pour notre build. J'ai essayé d'utiliser les phases de test pré-et post-intégration de maven, mais, si un test échoue, maven s'arrête et n'exécute pas le test post-intégration , ce qui n'est pas utile. "

vous pouvez essayer le plug-in à la place, je pense qu'il a la possibilité de garantir le nettoyage indépendamment de la configuration ou de l'état de l'étape intermédiaire

2
Binod Pant

Si vous ne voulez pas créer une suite et devez lister toutes vos classes de test, vous pouvez utiliser la réflexion pour trouver le nombre de classes de test dynamiquement et décompter dans une classe de base @AfterClass pour faire le tearDown une seule fois:

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

Si vous préférez utiliser @BeforeClass au lieu des blocs statiques, vous pouvez également utiliser un indicateur booléen pour effectuer le comptage de réflexion et tester la configuration une seule fois au premier appel. J'espère que cela aide quelqu'un, il m'a fallu un après-midi pour trouver une meilleure façon que d'énumérer toutes les classes dans une suite.

Il ne vous reste plus qu'à étendre cette classe à toutes vos classes de test. Nous avions déjà une classe de base pour fournir des éléments communs à tous nos tests, donc c'était la meilleure solution pour nous.

L'inspiration vient de cette SO réponse https://stackoverflow.com/a/37488620/5930242

Si vous ne voulez pas étendre cette classe partout, cette dernière réponse SO pourrait faire ce que vous voulez.

0
FredBoutin

Étant donné que maven-surefire-plugin n'exécute pas la classe Suite en premier mais traite les classes de suite et de test de la même manière, nous pouvons donc configurer le plugin comme ci-dessous pour activer uniquement les classes de suite et désactiver tous les tests. Suite exécutera tous les tests.

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

La seule façon, je pense, d'obtenir les fonctionnalités que vous souhaitez serait de faire quelque chose comme

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

J'utilise quelque chose comme ça dans Eclipse, donc je ne sais pas comment il est portable en dehors de cet environnement

0
Jroc

Pour autant que je sache, il n'existe aucun mécanisme pour le faire dans JUnit, mais vous pouvez essayer de sous-classer Suite et de remplacer la méthode run () avec une version qui fournit des hooks.

0
user15299