2009-08-05 17 views
12

Estamos usando JUnit 4 para probar: tenemos clases que no son una subclase de TestCase, y tienen métodos públicos anotados con @Test. Tenemos un archivo con muchos métodos @Test. Sería bueno para ser capaz de ejecutar un subconjunto de ellos a través de la hormiga de la línea de comandos, en el estilo de esta receta de JUnit 3:ejecutando un subconjunto de JUnit @Test métodos

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

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

He estado tratando de pensar formas de lograr esto con la reflexión de Java, etc. Dado que no parece haber ninguna forma de "ocultar" los métodos @Test ni eliminar sus anotaciones en tiempo de ejecución, la única opción parece ser utilizar el método defineClass de ClassLoader, lo que parece bastante difícil.

P.S. Lo correcto en esta situación sería dividir el archivo, pero ¿hay alternativas?

Gracias por su tiempo.

Respuesta

10

La solución de guerda es buena. Esto es lo que terminé haciendo (que es una mezcla de la receta de Lucas Francl, que he vinculado antes, y algunas otras cosas que vi en la red):

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

Entonces anotado mi clase de prueba con:

@RunWith(FilteredRunner.class) 
public class MyTest { 

y poner el siguiente en mi BuildFile hormiga:

<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 línea clave de estar allí la etiqueta sysproperty.

Y ahora puedo correr

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

lo deseas. Esto funciona con JUnit 4.1 --- en 4.4, subclase de JUnit4ClassRunner, y en 4.5 y posterior, subclase de BlockJUnit4ClassRunner.

+0

OK, eso es mucho más elegante que mi solución :) – guerda

+0

He estado luchando con este mismo problema (o al menos muy similar) durante unos días y hay una cosa que no deja de molestarme. ¿Qué sucede si quieres usar tu FilteredRunner con Powermock, que también requiere su propia anotación @RunWith (PowermockRunner.class)? –

+0

Gran respuesta, pero obsoleta ahora. Creo que se necesita usar BlockJUnit4ClassRunner en lugar de TestClassRunner – Illidanek

8

Crea tu propia TestClassMethodsRunner (no está documentada o no la encuentro ahora).
A TestClassMethodsRunner ejecuta todos los TestCases y puede configurar un filtro TestClassMethodsRunner.

Todo lo que tiene que hacer es anular el método TestMethodRunner createMethodRunner(Object, Method, RunNotifier). Este es un simple una solución 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); 
     } 
    } 

} 

Con esta TestRunner, ejecutar todas las pruebas que no contienen la cadena "NO". Otros serán ignorados :) Simplemente agregue la anotación @RunWith con su clase TestRunner a su prueba.

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

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

En el método createMethodRunner se puede comprobar la corriente de prueba en contra de una lista de las pruebas que deben ejecutarse o introducen nuevos criterios.

¡Buena suerte con esto!

¡Se aprecian sugerencias para una mejor solución!

12

Desde JUnit 4.8 tenemos anotaciones de @Category para resolver solo ese problema.

+2

@Category es bastante genial, pero solo si ya construye suites. Como el artículo menciona al final que no encaja en el entorno de prueba de todos. Sin embargo, definitivamente vale la pena señalar, ya que para algunos afortunados esto resolverá por completo su problema. –

+2

Y desde Surefire 2.11 (más o menos) podemos usar -Dgroups para seleccionar pruebas por categoría sin construir suites. ¡Me alegré de eliminar muchas de nuestras suites de prueba la semana pasada gracias a esta mejora! (El parámetro del plugin Maven Surefire solo está documentado para TestNG, pero desde 2.11 también funciona para JUnit). –

+0

Ese sitio parece estar inactivo ... –

1

Hay una manera más simple para el caso común donde se necesita ejecutar un solo método de ensayo, sin tener que pasar por la molestia de crear una costumbre Runner o 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. 

} 
Cuestiones relacionadas