55 votes

Comment créer dynamiquement une suite de tests dans JUnit 4 ?

Je voudrais créer une suite de test junit en utilisant JUnit 4 où les noms des classes de test à inclure ne sont pas connus avant l'exécution de la suite de test.

Dans JUnit 3, je pouvais faire cela :

public final class MasterTester extends TestCase
{
  /**
   * Used by junit to specify what TestCases to run.
   * 
   * @return a suite containing what TestCases to run
   */
  public static TestSuite suite() {
    TestSuite suite = new TestSuite();

    for(Class<?> klass : gatherTestClasses()) {
      suite.addTestSuite(klass);
    }

    return suite;
  }
}

et laisser les gatherTestClasses() permet de déterminer les classes de test à exécuter.

Dans JUnit 4, le documentation dit d'utiliser une annotation : @SuiteClasses({TestClass1.class, TestClass2.class...}) pour construire ma suite de tests. Il y a de nombreuses réponses SO qui montre comment faire. Malheureusement, les exemples que je vois ne semblent pas permettre de passer une liste de TestClasses générée dynamiquement.

Ce site Réponse SO a suggéré que je devrais sous-classer BlockJUnit4ClassRunner ce que je ne veux pas faire.

Les suites de test spécifiées dynamiquement semblent être quelque chose qui doit se trouver quelque part dans JUnit 4. Quelqu'un sait-il où ?

40voto

Danail Nachev Points 2863

Pour créer une suite de tests dynamique, vous devez utiliser la fonction @RunWith annotation. Il existe deux façons courantes de l'utiliser :

@RunWith(Suite.class)

Ceci vous permet de spécifier, quelles classes composent la suite de tests en question. Ceci est équivalent au style de JUnit 3 :

import junit.framework.TestSuite;
import junit.framework.TestCase;

public final class MasterTester extends TestCase {

  public static TestSuite suite() {
    TestSuite suite = new TestSuite();
    suite.addTestSuite(TestClass1.class);        
    suite.addTestSuite(TestClass2.class);
    // etc...
    return suite;
  }
}

La classe JUnit 4 équivalente sera :

import org.junit.runners.Suite;

@RunWith(Suite.class)
@SuiteClasses({TestClass1.class, TestClass2.class})
public final class MasterTester {

}

@RunWith(AllTests.class)

Cela vous permet de spécifier dynamiquement les tests qui composent la suite de tests. Si vos tests ne sont pas connus avant l'exécution, vous ne pouvez pas les spécifier dans les annotations. Vous pouvez utiliser cette construction à la place. Ainsi, si le code JUnit 3 est :

import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.framework.Test;

public final class MasterTester extends TestCase {

  public static TestSuite suite() {
    TestSuite suite = new TestSuite();
    for (Test test : findAllTestCasesRuntime()) {
      suite.addTest(test);
    }
    return suite;
  }
}

Le code équivalent de JUnit 4 sera :

import org.junit.runners.AllTests;
import junit.framework.TestSuite;
import junit.framework.Test;

@RunWith(AllTests.class)
public final class MasterTester {

  public static TestSuite suite() {
    TestSuite suite = new TestSuite();
    for (Test test : findAllTestCasesRuntime()) {
      suite.addTest(test);
    }
    return suite;
  }
}

0 votes

L'action dans cette boucle for semble erronée. Voulez-vous dire suite.addTest(test) ; ?

0 votes

Merci de le signaler. J'ai mis à jour les sources pour utiliser addTest() et addTestSuite().

0 votes

Pourquoi créez-vous un nouveau TestCase() ? Ne devriez-vous pas ajouter le test que vous avez trouvé ?

35voto

Andrejs Points 4235

J'ai essayé cela en utilisant JUnit 4.8 et cela fonctionne :

@RunWith(AllTests.class)
public class SomeTests
{
    public static TestSuite suite()
    {
        TestSuite suite = new TestSuite();

        suite.addTest(new JUnit4TestAdapter(Test1.class));
        suite.addTest(new JUnit4TestAdapter(Test2.class));

        return suite;
     }
}

1 votes

Que dois-je faire avec cette méthode ? Je veux que la suite devienne un enfant de ma suite parent. stackoverflow.com/questions/18834908

0 votes

J'ai essayé mais quand j'ajoute @BeforeClass l'annotation, le code à l'intérieur de @BeforeClass ne fonctionne pas. Une idée ?

0 votes

Qu'est-ce que JUnit4TestAdapter faire ?

26voto

JavaRocky Points 3597

J'ai trouvé la suite Classpath très utile lorsqu'elle est utilisée avec une convention de nommage sur mes classes de test.

https://github.com/takari/takari-cpsuite

Voici un exemple :

import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;

@RunWith(ClasspathSuite.class)
@ClassnameFilters({".*UnitTest"})
public class MySuite {
}

6 votes

La réponse d'Andrejs rend Classpath redondant, du moins pour la tâche de création dynamique de suites de tests.

0 votes

Il semble qu'il ne soit plus maintenu et qu'il ait beaucoup de problèmes avec les bibliothèques natives et autres.

6voto

Brad Cupit Points 2808

Je ne suis pas sûr de ce que fait gatherTestClasses(), mais disons qu'il renvoie certains tests lorsque le système d'exploitation est Linux et d'autres tests lorsque le système d'exploitation est Windows. Vous pouvez reproduire cela dans JUnit 4.4 avec hypothèses :

@Test
public void onlyOnLinux() {
    assumeThat(getOS(), is(OperatingSystem.LINUX));
    // rest of test
}

@Test
public void onlyOnWindows() {
    assumeThat(getOS(), is(OperatingSystem.WINDOWS));
    // rest of test
}

@Test
public void anyOperatingSystem() {
    // just don't call assumeThat(..)
}

La mise en œuvre de getOS() y OperatingSystem étant votre code personnalisé.

2 votes

Parfait, juste ce que je cherchais. Si vous utilisez apache commons-lang, vous pouvez utiliser "assumeTrue(SystemUtils.IS_OS_WINDOWS) ;".

0voto

Maher Abuthraa Points 736

Voici un exemple complet d'implémentation. Il combine deux classes de testCase et une suite.

  1. Exemple de test instrumenté :

    import android.support.test.rule.ActivityTestRule;
    
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.junit.runners.JUnit4;
    
    @RunWith(JUnit4.class)
    public class ExampleInstrumentedTest {
    
        @Rule
        public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);
    
        @Test
        public void checkInputs() throws Exception {
    
        }
    }
  2. Exemple de TestInstrumenté2 :

    import android.support.test.rule.ActivityTestRule;
    
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.junit.runners.JUnit4;
    
    @RunWith(JUnit4.class)
    public class ExampleInstrumentedTest2 {
    
        @Rule
        public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);
    
        @Test
        public void checkInputs() throws Exception {
    
        }
    }
  3. ExempleSuiteInstrumentée :

    import junit.framework.TestSuite;
    
    import org.junit.runner.RunWith;
    import org.junit.runners.AllTests;
    
    @RunWith(AllTests.class)
    public class ExampleInstrumentedSuite {
    
        public static TestSuite suite() {
            TestSuite suite = new TestSuite();
            suite.addTest(new junit.framework.JUnit4TestAdapter(ExampleInstrumentedTest.class));
            suite.addTest(new junit.framework.JUnit4TestAdapter(ExampleInstrumentedTest2.class));
            return suite;
        }
    }

Notez que vous devez utiliser @RunWith(JUnit4.class) au lieu de la valeur par défaut @RunWith(AndroidJUnit4.class) dans la classe testCase

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X