2009-08-05 6 views
12

Nous utilisons JUnit 4 pour tester: nous avons des classes qui ne sont pas une sous-classe de TestCase, et elles ont des méthodes publiques annotées avec @Test. Nous avons un fichier avec beaucoup de méthodes @Test. Il serait agréable d'être en mesure d'exécuter un sous-ensemble d'entre eux via Ant à partir de la ligne de commande, dans le style de cette recette pour JUnit 3:exécution d'un sous-ensemble de méthodes JUnit @Test

ant runtest -Dtest=MyTest -Dtests=testFoo,testBar 

http://today.java.net/pub/a/today/2003/09/12/individual-test-cases.html

J'ai essayé de penser Comme il ne semble pas possible de "cacher" les méthodes @Test ou de supprimer leurs annotations au moment de l'exécution, la seule option semble être d'utiliser la méthode defineClass du ClassLoader, ce qui semble assez difficile.

P.S. La bonne chose dans cette situation serait de diviser le fichier, mais existe-t-il des alternatives?

Merci pour votre temps.

Répondre

10

La solution de guerda est bonne. Voici ce que je fini par faire (c'est un mélange de recette de Luc Francl, que je lié avant, et quelques autres trucs que j'ai vu sur le net):

import org.junit.runner.manipulation.Filter; 
import org.junit.runner.Description; 

public final class AntCLFilter extends Filter { 
    private static final String TEST_CASES = "tests"; 
    private static final String ANT_PROPERTY = "${tests}"; 
    private static final String DELIMITER = "\\,"; 
    private String[] testCaseNames; 

    public AntCLFilter() { 
     super(); 
     if (hasTestCases()) testCaseNames = getTestCaseNames(); 
    } 

    public String describe() { 
     return "Filters out all tests not explicitly named in a comma-delimited list in the system property 'tests'."; 
    } 

    public boolean shouldRun(Description d) { 
     String displayName = d.getDisplayName(); 
     // cut off the method name: 
     String testName = displayName.substring(0, displayName.indexOf('(')); 
     if (testCaseNames == null) return true; 

     for (int i = 0; i < testCaseNames.length; i++) 
      if (testName.equals(testCaseNames[i])) 
       return true; 
     return false; 
    } 

    /** 
    * Check to see if the test cases property is set. Ignores Ant's 
    * default setting for the property (or null to be on the safe side). 
    **/ 
    public static boolean hasTestCases() { 
     return 
      System.getProperty(TEST_CASES) == null || 
      System.getProperty(TEST_CASES).equals(ANT_PROPERTY) ? 
      false : true; 
    } 

    /** 
    * Create a List of String names of test cases specified in the 
    * JVM property in comma-separated format. 
    * 
    * @return a List of String test case names 
    * 
    * @throws NullPointerException if the TEST_CASES property 
    * isn't set 
    **/ 
    private static String[] getTestCaseNames() { 

     if (System.getProperty(TEST_CASES) == null) { 
      throw new NullPointerException("Test case property is not set"); 
     } 

     String testCases = System.getProperty(TEST_CASES); 
     String[] cases = testCases.split(DELIMITER); 

     return cases; 
    } 
} 

import org.junit.internal.runners.*; 
import org.junit.runner.manipulation.Filter; 
import org.junit.runner.manipulation.NoTestsRemainException; 

public class FilteredRunner extends TestClassRunner { 

    public FilteredRunner(Class<?> clazz) throws InitializationError { 
     super(clazz); 
     Filter f = new AntCLFilter(); 
     try { 
      f.apply(this); 
     } catch (NoTestsRemainException ex) { 
      throw new RuntimeException(ex); 
     } 
    } 
} 

J'annotées ma classe de test avec:

@RunWith(FilteredRunner.class) 
public class MyTest { 

et mettre ce qui suit dans ma fourmi buildfile:

<target name="runtest" 
     description="Runs the test you specify on the command line with -Dtest=" 
     depends="compile, ensure-test-name"> 
    <junit printsummary="withOutAndErr" fork="yes"> 
     <sysproperty key="tests" value="${tests}" /> 
     <classpath refid="classpath" /> 
     <formatter type="plain" usefile="false" /> 
     <batchtest> 
      <fileset dir="${src}"> 
       <include name="**/${test}.java" /> 
      </fileset> 
     </batchtest> 
    </junit> 
</target> 

la ligne clé étant là la balise sysproperty.

Et maintenant, je peux courir

ant runtest -Dtest=MyTest -Dtests=testFoo,testBar 

comme vous le souhaitez. Cela fonctionne avec JUnit 4.1 --- 4.4, sous-classe de JUnit4ClassRunner, et dans 4.5 et plus tard, sous-classe de BlockJUnit4ClassRunner.

+0

OK, c'est beaucoup plus élégant que ma solution :) – guerda

+0

J'ai lutté avec ce même problème (ou au moins très similaire) pendant quelques jours et il y a une chose qui ne cesse de me déranger. Que faire si vous souhaitez utiliser votre FilteredRunner avec Powermock, qui nécessite également sa propre annotation @RunWith (PowermockRunner.class)? –

+0

Bonne réponse, mais démodé maintenant. Je pense que BlockJUnit4ClassRunner doit être utilisé à la place de TestClassRunner – Illidanek

8

Créez votre propre TestClassMethodsRunner (ce n'est pas documenté ou je ne le trouve pas maintenant).
Un TestClassMethodsRunner exécute tous les TestCases et vous pouvez configurer un TestClassMethodsRunner filtré.

Tout ce que vous avez à faire est de surcharger la méthode TestMethodRunner createMethodRunner(Object, Method, RunNotifier). Ceci est simple une solution aki:

public class FilteredTestRunner extends TestClassMethodsRunner { 

    public FilteredTestRunner(Class<?> aClass) { 
     super(aClass); 
    } 

    @Override 
    protected TestMethodRunner createMethodRunner(Object aTest, Method aMethod, RunNotifier aNotifier) { 
     if (aTest.getClass().getName().contains("NOT")) { 
      return new TestMethodRunner(aTest, aMethod, aNotifier, null) { 
       @Override 
       public void run() { 
        //do nothing with this test. 
       } 
      }; 
     } else { 
      return super.createMethodRunner(aTest, aMethod, aNotifier); 
     } 
    } 

} 

Avec cette TestRunner, vous exécutez tous les tests qui ne contiennent pas la chaîne "NOT". Les autres seront ignorés :) Ajoutez simplement l'annotation @RunWith à votre test avec votre classe TestRunner.

@RunWith(FilteredTestRunner.class) 
public class ThisTestsWillNOTBeExecuted { 
    //No test is executed. 
} 

@RunWith(FilteredTestRunner.class) 
public class ThisTestsWillBeExecuted { 
    //All tests are executed. 
} 

Dans la méthode createMethodRunner vous pouvez vérifier le test en cours contre une liste de tests qui doivent être exécutés ou de nouveaux critères introduisent.

Bonne chance avec ça!

Des conseils pour une meilleure solution sont appréciés!

12

Depuis JUnit 4.8, nous avons des annotations @Category pour résoudre ce problème.

+2

@Category est plutôt cool, mais seulement si vous avez déjà construit des suites. Comme l'indique l'article à la fin, cela ne rentre pas dans l'environnement de test de tout le monde. Ils valent certainement la peine d'être signalés, car pour certains chanceux, cela résoudra complètement leur problème. –

+2

Et depuis Surefire 2.11 (ou plus) nous pouvons utiliser -Dgroups pour sélectionner les tests par catégorie sans construire de suites. J'ai été heureux de supprimer beaucoup de nos suites de test la semaine dernière grâce à cette amélioration! (Le paramètre du plugin Maven Surefire est uniquement documenté pour TestNG, mais depuis 2.11 fonctionne également pour JUnit.) –

+0

Ce site semble être en panne ... –

1

Il existe un moyen plus simple pour le cas commun où vous devez exécuter une seule méthode d'essai, sans avoir à passer par les tracas de créer une coutume Runner ou Filter:

public class MyTestClass { 

    public static void main(final String[] args) throws Exception { 
    final JUnitCore junit = new JUnitCore(); 
    final String singleTest = // Get the name of test from somewhere (environment, system property, whatever you want). 
    final Request req; 
    if (singleTest != null) { 
     req = Request.method(MyTestClass.class, singleTest); 
    } else { 
     req = Request.aClass(MyTestClass.class); 
    } 
    final Result result = junit.run(req); 
    // Check result.getFailures etc. 
    if (!result.wasSuccessful()) { 
     System.exit(1); 
    } 
    } 

    // Your @Test methods here. 

} 
Questions connexes