2010-02-01 9 views
66

JUnit 4.8 contiene una nueva característica llamada "Categorías" que le permite agrupar ciertos tipos de pruebas juntas. Esto es muy útil, p. tener pruebas de prueba separadas para pruebas lentas y rápidas. Sé lo mencionado en JUnit 4.8 release notes, pero me gustaría saber cómo puedo ejecutar todas las pruebas anotadas en cierta categoría.Cómo ejecutar todas las pruebas que pertenecen a una determinada categoría en JUnit 4

Los JUnit 4.8 notas de la versión muestran un ejemplo de definición de conjunto, donde SuiteClasses anotación selecciona las pruebas de cierta categoría para funcionar, así:

@RunWith(Categories.class) 
@IncludeCategory(SlowTests.class) 
@SuiteClasses({ A.class, B.class }) // Note that Categories is a kind of Suite 
public class SlowTestSuite { 
    // Will run A.b and B.c, but not A.a 
} 

¿Alguien sabe cómo podría ejecutar todas las pruebas en la categoría SlowTests ? Parece que debe tener la anotación SuiteClasses ...

+1

Hola. Tengo una pregunta relacionada. siéntase libre de tocar en: http://stackoverflow.com/questions/15776718/using-junit-categories-vs-simply-organizing-logical-test-categories-in-separate – amphibient

Respuesta

59

Descubrí una forma posible de lograr lo que quiero, pero no considero que esta sea la mejor solución posible ya que se basa en la biblioteca ClassPathSuite que no es parte de JUnit.

que definir el conjunto de pruebas para las pruebas lentas como esto:

clase
@RunWith(Categories.class) 
@Categories.IncludeCategory(SlowTests.class) 
@Suite.SuiteClasses({ AllTests.class }) 
public class SlowTestSuite { 
} 

alltests se define así:

@RunWith(ClasspathSuite.class) 
public class AllTests { 
} 

tuve que usar la clase ClassPathSuite de ClassPathSuite proyecto aquí. Encontrará todas las clases con pruebas.

+4

En realidad es una solución bastante razonable. Gracias por enviarnos su propia pregunta, ya que es muy buena :-) –

+0

Para cualquiera que se pregunte cómo automatizar la ejecución de una categoría de pruebas (con esta configuración exacta) usando Ant, [esta pregunta] (http://stackoverflow.com/questions/6226026/how-to-run-all-junit-tests-in-a-category-suite-with-ant) puede ser útil. – Jonik

+3

Como complemento a mi pregunta http://stackoverflow.com/q/2698174/59470 y explicación detallada, agregué una entrada de blog: http://novyden.blogspot.com/2011/06/using-junit-4-categories -para reemplazar.html – topchef

1

No estoy seguro, cuál es exactamente su problema.

Simplemente agregue todas las pruebas a un conjunto (o hirachy de suites). A continuación, utilice la anotación Categorías Runner e Include/ExcludeCategory para especificar las categorías que desea ejecutar.

Una buena idea podría ser tener una suite que contenga todas las pruebas, y un par de suites separadas refiriéndose a la primera, especificando el conjunto diferente de categorías que necesita.

+19

Mi problema es que tengo miles de pruebas y No quiero agregarlos manualmente a ninguna suite. Solo quiero que se ejecuten las pruebas con cierta categoría. No debería ser tan difícil para JUnit averiguar qué pruebas tienen cierta anotación, ya que de hecho lo hace de todos modos al encontrar métodos de prueba. – Kaitsu

1
No

una respuesta directa a su problema, pero tal vez el enfoque general podría mejorarse ...

Por qué son lentos sus pruebas? Tal vez la configuración dure mucho tiempo (base de datos, E/S, etc.), ¿tal vez las pruebas están probando demasiado? Si este es el caso, separaría las pruebas unitarias reales de las de "larga duración", que a menudo son pruebas de integración.

En mis configuraciones, tengo staging env, donde las pruebas unitarias se ejecutan con frecuencia y las pruebas de integración constantemente, pero más raramente (por ejemplo, después de cada confirmación en el control de la versión). Nunca he trabajado con la agrupación para las pruebas unitarias, porque deberían estar ligeramente acopladas. Solo trabajo con agrupamiento y relación de casos de prueba en configuraciones de prueba de integración (pero con TestNG).

Pero es bueno saber que JUnit 4.8 introdujo algunas características de agrupación.

+1

Gracias Manuel por sus comentarios! Realmente no necesito separar las pruebas unitarias, pero uso JUnit también para las pruebas de integración y quiero separarlas de las pruebas unitarias. También miré en TestNG y parece hacer que las pruebas (y no solo las pruebas unitarias) sean más agradables que JUnit. Y también tiene una mejor documentación y un buen libro. – Kaitsu

7

Estas son algunas de las principales diferencias entre TestNG y JUnit cuando se trata de grupos (o categorías, como JUnit los llama):

  • JUnit de se escriben (anotaciones), mientras que de TestNG son cadenas. Hice esta elección porque quería poder usar expresiones regulares al ejecutar pruebas, por ejemplo, "ejecutar todas las pruebas que pertenecen al grupo" de la base de datos * ".Además, tener que crear una nueva anotación cada vez que necesite crear una nueva categoría es molesto, aunque tiene la ventaja de que un IDE le dirá de inmediato dónde se usa esta categoría (TestNG le muestra esto en sus informes).

  • TestNG separa muy claramente su modelo estático (el código de sus pruebas) del modelo de tiempo de ejecución (cuyas pruebas se ejecutan). Si desea ejecutar primero los grupos "front-end" y luego "servlets", puede hacerlo sin tener que volver a compilar nada. Debido a que JUnit define grupos en anotaciones y necesita especificar estas categorías como parámetros para el corredor, generalmente tiene que volver a compilar su código cada vez que desee ejecutar un conjunto diferente de categorías, lo que en mi opinión es contrario al propósito.

+0

Creamos nuestro propio soporte de categorías en nuestras pruebas JUnit de una manera muy similar a JUnit, la principal diferencia es que en lugar de la anotación @ Categories.IncludeCategory, hicimos la nuestra configurable a través de una propiedad del sistema. No se sabe por qué esto fue demasiado difícil para JUnit para nosotros. – Trejkaz

2

Para ejecutar pruebas categorizados sin especificar todos ellos explicily en @Suite.SuiteClasses anotación que puede proporcionar su propia implementación de la Suite. Por ejemplo, se puede extender un org.junit.runners.ParentRunner. En lugar de utilizar una matriz de clases proporcionada por @Suite.SuiteClasses, la nueva implementación debe realizar la búsqueda de pruebas categorizadas en classpath.

Vea this project como un ejemplo de tal enfoque. Uso:

@Categories(categoryClasses = {IntegrationTest.class, SlowTest.class}) 
@BasePackage(name = "some.package") 
@RunWith(CategorizedSuite.class) 
public class CategorizedSuiteWithSpecifiedPackage { 

} 
5

Una desventaja de la solución de Kaitsu es que se Eclipse ejecutar las pruebas dos veces, y las SlowTests 3 veces, cuando se ejecuta todas las pruebas en un proyecto. Esto se debe a que Eclipse ejecutará todas las pruebas, luego la suite AllTests y luego la SlowTestSuite.

Aquí hay una solución que implica la creación de subclases de los corredores de prueba de la solución Kaitsu para omitir las suites a menos que se establezca una determinada propiedad del sistema. Un hack vergonzoso, pero todo lo que he encontrado hasta ahora.

@RunWith(DevFilterClasspathSuite.class) 
public class AllTests {} 

.

@RunWith(DevFilterCategories.class) 
@ExcludeCategory(SlowTest.class) 
@SuiteClasses(AllTests.class) 
public class FastTestSuite 
{ 
} 

.

public class DevFilterCategories extends Suite 
{ 
    private static final Logger logger = Logger 
     .getLogger(DevFilterCategories.class.getName()); 
    public DevFilterCategories(Class<?> suiteClass, RunnerBuilder builder) throws InitializationError { 
     super(suiteClass, builder); 
     try { 
      filter(new CategoryFilter(getIncludedCategory(suiteClass), 
        getExcludedCategory(suiteClass))); 
      filter(new DevFilter()); 
     } catch (NoTestsRemainException e) { 
      logger.info("skipped all tests"); 
     } 
     assertNoCategorizedDescendentsOfUncategorizeableParents(getDescription()); 
    } 

    private Class<?> getIncludedCategory(Class<?> klass) { 
     IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class); 
     return annotation == null ? null : annotation.value(); 
    } 

    private Class<?> getExcludedCategory(Class<?> klass) { 
     ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class); 
     return annotation == null ? null : annotation.value(); 
    } 

    private void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError { 
     if (!canHaveCategorizedChildren(description)) 
      assertNoDescendantsHaveCategoryAnnotations(description); 
     for (Description each : description.getChildren()) 
      assertNoCategorizedDescendentsOfUncategorizeableParents(each); 
    } 

    private void assertNoDescendantsHaveCategoryAnnotations(Description description) throws InitializationError {   
     for (Description each : description.getChildren()) { 
      if (each.getAnnotation(Category.class) != null) 
       throw new InitializationError("Category annotations on Parameterized classes are not supported on individual methods."); 
      assertNoDescendantsHaveCategoryAnnotations(each); 
     } 
    } 

    // If children have names like [0], our current magical category code can't determine their 
    // parentage. 
    private static boolean canHaveCategorizedChildren(Description description) { 
     for (Description each : description.getChildren()) 
      if (each.getTestClass() == null) 
       return false; 
     return true; 
    } 
} 

.

public class DevFilterClasspathSuite extends ClasspathSuite 
{ 
    private static final Logger logger = Logger 
     .getLogger(DevFilterClasspathSuite.class.getName()); 
    public DevFilterClasspathSuite(Class<?> suiteClass, RunnerBuilder builder) 
     throws InitializationError { 
     super(suiteClass, builder); 
     try 
     { 
      filter(new DevFilter()); 
     } catch (NoTestsRemainException e) 
     { 
      logger.info("skipped all tests"); 
     } 
    } 
} 

.

public class DevFilter extends Filter 
{ 
    private static final String RUN_DEV_UNIT_TESTS = "run.dev.unit.tests"; 

    @Override 
    public boolean shouldRun(Description description) 
    { 
     return Boolean.getBoolean(RUN_DEV_UNIT_TESTS); 
    } 

    @Override 
    public String describe() 
    { 
     return "filter if "+RUN_DEV_UNIT_TESTS+" system property not present"; 
    } 
} 

Así, en su lanzador FastTestSuite, sólo tiene que añadir -Drun.dev.unit.tests = true a los argumentos de VM. (Tenga en cuenta que esta solución hace referencia a un conjunto de pruebas rápidas en lugar de uno lento).

Cuestiones relacionadas