java - tutorial - junit maven
Cómo ejecutar todas las pruebas pertenecientes a una determinada categoría en JUnit 4 (6)
JUnit 4.8 contiene una nueva característica llamada "Categorías" que le permite agrupar ciertos tipos de pruebas. Esto es muy útil, por ejemplo, para ejecutar pruebas separadas para pruebas lentas y rápidas. Conozco las cosas mencionadas en las notas de la versión JUnit 4.8 , pero me gustaría saber cómo puedo ejecutar todas las pruebas anotadas en cierta categoría.
Las notas de la versión de JUnit 4.8 muestran un ejemplo de definición de suite, donde la anotación de SuiteClasses selecciona las pruebas de cierta categoría para ejecutar, como esta:
@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 debes tener la anotación SuiteClasses ...
Descubrí una forma posible de lograr lo que quiero, pero no considero que sea la mejor solución posible ya que depende de la biblioteca ClassPathSuite que no forma parte de JUnit.
Defino el conjunto de pruebas para pruebas lentas como esta:
@RunWith(Categories.class)
@Categories.IncludeCategory(SlowTests.class)
@Suite.SuiteClasses( { AllTests.class })
public class SlowTestSuite {
}
La clase AllTests se define así:
@RunWith(ClasspathSuite.class)
public class AllTests {
}
Tuve que usar la clase ClassPathSuite proyecto ClassPathSuite aquí. Encontrará todas las clases con pruebas.
Estas son algunas de las principales diferencias entre TestNG y JUnit cuando se trata de grupos (o categorías, como JUnit los llama):
Los JUnit se escriben (anotaciones) mientras que TestNG son cadenas. Hice esta elección porque quería poder utilizar 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 siempre que necesite crear una nueva La categoría es molesta, aunque tiene el beneficio de que un IDE le dirá de inmediato dónde se usa esta categoría (TestNG lo muestra 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.
No es una respuesta directa a su problema, pero tal vez el enfoque general podría mejorarse ...
¿Por qué tus pruebas son lentas? 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 un entorno intermedio, donde las pruebas unitarias se ejecutan con frecuencia y las pruebas de integración constantemente pero con menos frecuencia (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.
No estoy seguro, cuál es exactamente tu 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 que se refieran a la primera, especificando el conjunto diferente de categorías que necesita.
Para ejecutar pruebas categorizadas sin especificarlas explícitamente en la anotación @Suite.SuiteClasses
, puede proporcionar su propia implementación de Suite. Por ejemplo, org.junit.runners.ParentRunner
se puede extender. En lugar de utilizar una matriz de clases proporcionada por @Suite.SuiteClasses
, la nueva implementación debería realizar búsquedas de pruebas clasificadas en classpath.
Vea este proyecto como un ejemplo de tal enfoque. Uso:
@Categories(categoryClasses = {IntegrationTest.class, SlowTest.class})
@BasePackage(name = "some.package")
@RunWith(CategorizedSuite.class)
public class CategorizedSuiteWithSpecifiedPackage {
}
Una desventaja de la solución de Kaitsu es que Eclipse ejecutará sus pruebas dos veces, y las pruebas lentas 3 veces, al ejecutar 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 de 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";
}
}
Por lo tanto, en su iniciador FastTestSuite, solo agregue -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).