2009-08-05 13 views
12

Do testowania używamy JUnit 4: mamy klasy, które nie są podklasą TestCase i mają publiczne metody opatrzone przypisami @Test. Mamy jeden plik z wieloma metodami @Test. Byłoby miło, aby móc uruchomić podzbiór nich poprzez Ant z wiersza poleceń w stylu tego przepisu na JUnit 3:uruchamianie podzbioru metod JUnit @Test

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

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

Próbowałam myśleć sposoby na osiągnięcie tego przy pomocy refleksji w Javie itp. Ponieważ nie ma sposobu, aby "ukryć" metody lub usunąć ich adnotacje w czasie wykonywania, jedyną opcją wydaje się być użycie metody ClassLoader's defineClass, która wydaje się dość trudna.

P.S. W tej sytuacji właściwym rozwiązaniem byłoby rozdzielenie pliku, ale czy istnieją alternatywy?

Dzięki za poświęcony czas.

Odpowiedz

10

Rozwiązanie guerdy jest dobre. Oto, co skończyło się robi (jest to mieszanka receptury Luke'a Francl, która I połączone wcześniej, i kilka innych rzeczy widziałem w internecie):

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

Potem adnotacją moją klasę testową z:

@RunWith(FilteredRunner.class) 
public class MyTest { 

i umieścić następujące w moim mrówek 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> 

kluczem linia istnienia tag sysproperty.

A teraz mogę uruchomić

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

jako pożądane. Działa to z JUnit 4.1 --- w 4.4, podklasę z JUnit4ClassRunner, oraz w 4.5 i późniejszą, podklasę z BlockJUnit4ClassRunner.

+0

OK, to jest o wiele bardziej elegancki niż moje rozwiązanie :) – guerda

+0

Przez kilka dni zmagałem się z tym samym (lub co najmniej bardzo podobnym) problemem i jest jedna rzecz, która przeszkadza mi. Co zrobić, jeśli chcesz użyć FilteredRunner z Powermock, który wymaga również własnej adnotacji @RunWith (PowermockRunner.class)? –

+0

Świetna odpowiedź, ale teraz przestarzała. Myślę, że BlockJUnit4ClassRunner musi być użyty zamiast TestClassRunner – Illidanek

8

Stwórz swój własny TestClassMethodsRunner (nie jest to dokumentacja lub teraz go nie znajduję).
A TestClassMethodsRunner wykonuje wszystkie TestCases i można skonfigurować filtrowany TestClassMethodsRunner.

Wszystko, co musisz zrobić, to zastąpić metodę TestMethodRunner createMethodRunner(Object, Method, RunNotifier). Jest to proste rozwiązanie hacky:

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

} 

Dzięki tej TestRunner, wykonać wszystkie testy, które nie zawierają napis „NIE”. Inne będą ignorowane :) Po prostu dodaj adnotację @RunWith do swojej klasy TestRunner.

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

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

W metodzie createMethodRunner można sprawdzić bieżący test z listą testów, które muszą być wykonane lub wprowadzenia nowych charakterystyczne.

Życzymy powodzenia!

Wskazówki dotyczące ładniejszego rozwiązania są mile widziane!

12

Od JUnit 4.8 mamy adnotacje @Kategorii, które rozwiązują właśnie ten problem.

+2

@Kategoria jest całkiem fajna, ale tylko wtedy, gdy już budujesz pakiety. Jak wspomina w końcu artykuł, który nie pasuje do środowiska testowego każdego. Są zdecydowanie warte podkreślenia, jak w przypadku niektórych szczęśliwych ludzi, to całkowicie rozwiąże ich problem. –

+2

A ponieważ Surefire 2.11 (lub tak) możemy użyć -Dgroups, aby wybrać testy według kategorii bez pakietów budynków. Byłem szczęśliwy mogąc usunąć wiele naszych zestawów testowych w zeszłym tygodniu dzięki tej poprawie! (Parametr wtyczki Maven Surefire jest tylko udokumentowany dla TestNG, ale ponieważ 2.11 działa również dla JUnit.) –

+0

Ta strona wydaje się być niedostępna ... –

1

Jest prostszy sposób dla zwykłego przypadku, gdy trzeba uruchomić tylko jedną metodę badania, bez konieczności przechodzenia przez kłopotów tworzenia niestandardowych Runner lub 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. 

} 
Powiązane problemy