2009-08-05 25 views
12

Wir verwenden JUnit 4 zum Testen: Wir haben Klassen, die keine Unterklasse von TestCase sind, und sie haben öffentliche Methoden, die mit @Test kommentiert sind. Wir haben eine Datei mit vielen @Test Methoden. Es wäre schön, in der Lage sein eine Teilmenge von ihnen über Ant von der Kommandozeile, in der Stil dieses Rezept für JUnit 3 auszuführen:Ausführen einer Teilmenge von JUnit @Test-Methoden

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

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

Ich habe versucht, zu denken, Wege, dies mit Java-Reflektion, etc. zu erreichen. Da es scheinbar keine Möglichkeit gibt, @Test Methoden zu "verstecken" oder ihre Annotationen zur Laufzeit zu entfernen, scheint die einzige Methode die defineClass Methode des ClassLoader zu sein, was ziemlich schwierig erscheint.

P.S. Die richtige Sache in dieser Situation wäre, die Datei aufzuteilen, aber gibt es Alternativen?

Danke für Ihre Zeit.

Antwort

10

Guerda-Lösung ist gut. Hier ist, was ich am Ende tun (es ist eine Mischung aus Luke Francl Rezept, das ich verbunden, und einige andere Sachen, die ich auf dem Netz sah):

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

Dann kommentierte ich meine Testklasse mit:

@RunWith(FilteredRunner.class) 
public class MyTest { 

und fügen Sie die folgende in meiner ant 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> 

der Schlüssel Linie dort zu sein der sysproperty Tag.

Und jetzt kann ich

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

wie gewünscht ausgeführt werden. Dies funktioniert mit JUnit 4.1 --- in 4.4, Unterklasse von JUnit4ClassRunner, und in 4.5 und später, Unterklasse von BlockJUnit4ClassRunner.

+0

OK, das ist viel eleganter als meine Lösung :) – guerda

+0

Ich habe mit diesem gleichen (oder zumindest sehr ähnlichen) Problem für ein paar Tage gekämpft und es gibt eine Sache, die mich belästigt. Was ist, wenn Sie Ihren FilteredRunner mit Powermock verwenden möchten, der auch eine eigene @RunWith (PowermockRunner.class) -Anmerkung benötigt? –

+0

Große Antwort, aber jetzt veraltet. Ich denke BlockJUnit4ClassRunner muss anstelle von TestClassRunner verwendet werden – Illidanek

8

Erstellen Sie Ihre eigenen TestClassMethodsRunner (es ist nicht dokumentiert ist oder ich es jetzt nicht finden).
A TestClassMethodsRunner führt alle Testfälle und Sie können eine gefilterte TestClassMethodsRunner einrichten.

Alles was Sie tun müssen, ist die TestMethodRunner createMethodRunner(Object, Method, RunNotifier) Methode außer Kraft setzen. Dies ist eine einfache eine hacky Lösung:

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

} 

Mit diesem Testrunner, Sie alle Tests ausführen, die die Zeichenfolge „NOT“ nicht enthalten. Andere werden ignoriert :) Fügen Sie einfach die @RunWith Annotation mit Ihrer TestRunner-Klasse zu Ihrem Test hinzu.

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

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

Im createMethodRunner Methode können Sie den aktuellen Test gegen eine Liste von Tests überprüfen, die ausgeführt werden müssen oder neue Kriterien einführen.

Viel Glück damit!

Hinweise für eine schönere Lösung zu schätzen!

12

Seit JUnit 4.8 haben wir @Category Anmerkungen genau das Problem zu lösen.

+2

@Category ist ziemlich cool, aber nur, wenn Sie bereits Suiten erstellen. Wie der Artikel am Ende erwähnt, passt das nicht in jedermanns Testumgebung. Sie sind definitiv wert, darauf hinzuweisen, aber für einige glückliche Leute wird dies ihr Problem vollständig lösen. –

+2

Und seit Surefire 2.11 (oder so) können wir -Dgroups verwenden, um Tests nach Kategorie auszuwählen, ohne Suites zu erstellen. Ich war glücklich, letzte Woche dank dieser Verbesserung eine Menge unserer Testsuiten zu löschen! (Der Parameter von Maven Surefire Plugin ist nur für TestNG dokumentiert, aber seit 2.11 funktioniert auch für JUnit.) –

+0

Diese Seite scheint herunterzufallen ... –

1

es einen einfacheren Weg für den gemeinsamen Fall ist, wo Sie brauchen nur eine Testmethode laufen, ohne Runner oder Filter der Schaffung eines benutzerdefinierten durch den Aufwand zu gehen:

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. 

} 
Verwandte Themen