web-dev-qa-db-fra.com

JUNIT: lancez l’installation une seule fois pour un grand nombre de classes de test

J'ai un cours que j'utilise comme base pour mes tests unitaires. Dans cette classe, j'initialise l'environnement complet de mes tests, en configurant des mappages de base de données, en saisissant plusieurs enregistrements de base de données sur plusieurs tables, etc. Cette classe 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.

Ma question est, puisque la classe avant est exactement la même pour toutes ces classes de test, comment puis-je m'assurer qu'elles ne sont exécutées qu'une seule fois pour tous les tests. Une solution simple est que je puisse garder tous les tests dans une classe. Cependant, le nombre de tests est énorme, ils sont également classés en fonction des têtes fonctionnelles. Donc, ils sont situés dans des classes différentes. Cependant, puisqu'ils ont besoin de la même configuration, ils héritent de @BeforeClass. En conséquence, toute la configuration est effectuée au moins une fois par classe de test, ce qui prend beaucoup plus de temps que je ne le souhaiterais. 

Je pourrais cependant les mettre tous dans divers sous-paquets sous un même package. Par conséquent, s'il existe un moyen, comment je peux exécuter la configuration une fois pour tous les tests de ce package, ce serait formidable.

19
neoInfinite

Avec la suite de tests JUnit4, vous pouvez faire quelque chose comme ceci:

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

Ensuite, vous exécutez cette classe comme vous exécuteriez une classe de test normale et tous vos tests seront exécutés.

17
FredBoutin

JUnit ne prend pas cela en charge, vous devrez utiliser les solutions standard Java pour les singletons: déplacez le code d'installation commun dans un bloc de code statique, puis appelez une méthode vide dans cette classe:

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

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

Assurez-vous que tous les tests appellent init(), par exemple ma mise en place dans une méthode @BeforeClass. Ou placez le bloc de code statique dans une classe de base partagée.

Sinon, utilisez une variable globale:

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

     ...init code here...
 }
6
Aaron Digulla

Vous pouvez créer une classe BaseTest avec une méthode @BeforeClass, puis en faire hériter tous les autres tests. De cette façon, lorsque chaque objet de test est construit, @BeforeClass est exécuté. 

Évitez également de l'exécuter une seule fois pour toute la suite de tests, car tous les cas de tests doivent être indépendants. @BeforeClass ne doit être exécuté qu'une fois par cas de test, pas une suite de tests.

1
fedeb

Créez une classe de base pour tous les tests:

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

et chaque test devrait en hériter:

public class SomeTest extends BaseTest {

}
1
Filip

Je ne sais pas si quelqu'un utilise encore JUnit et essaie de le réparer sans utiliser Spring Runner (autrement dit pas d'intégration Spring). TestNG a cette fonctionnalité. Mais voici une solution basée sur JUnit.

Créez une opération RunOnce par thread comme ceci. Ceci maintient une liste des classes pour lesquelles l'opération a été exécutée.

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

De retour dans votre test unitaire

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

Lorsque vous utilisez cette méthode, vous pouvez effectuer des opérations une fois par classe à l'aide de ThreadLocal.

0
skipy

Si vous pouvez tolérer l'ajout de spring-test à votre projet ou si vous l'utilisez déjà, utilisez la technique décrite ici: Comment charger les données de test DBUnit une fois par cas avec Spring Test

0
Kkkev