Obtener un recuento de la cantidad de veces que aparezca un signo @Category en un conjunto de pruebas de JUnit

MivaScott:

He desarrollado una serie completa de pruebas automatizadas usando Java, selenio, Junit, Maven.

Para cada prueba, que tienen uno o más @Category anotaciones que describen qué área del software que cubre cada prueba. Por ejemplo:

@Test
@Category({com.example.core.categories.Priority1.class,
           com.example.core.categories.Export.class,
           com.example.core.categories.MemberData.class})


@Test
@Category({com.example.core.categories.Priority1.class,
           com.example.core.categories.Import.class,
           com.example.core.categories.MemberData.class})


@Test
@Ignore
@Category({com.example.core.categories.Priority2.class,
           com.example.core.categories.Import.class,
           com.example.core.categories.MemberData.class})

Lo que estoy tratando de hacer es encontrar una manera de obtener un conteo de la cantidad de pruebas contienen ninguna categoría dada. Todas las categorías posibles son los nombres de archivo en la //com/example/core/categoriescarpeta como una lista de fuentes.

He intentado construir un script de shell para hacer un recuento de palabras, que parece funcionar bien, pero yo creo que habría algo más "built-in" para hacer frente a @Category.

Mi mayor problema es que incluso si consigo el recuento de la derecha, es muy posible que una o más de las pruebas están marcados @ignore que debe anular que las pruebas @ Categoría de pero sin un uso intensivo de banderas y la lectura de todos los archivos línea por línea con el fin que arroja la cuenta correcta.

¿Hay una buena manera de detallar @ Categoría es que también son factores en @ignore?

Ejemplo de salida

| Category                                     | Count |
|----------------------------------------------|------:|
| com.example.core.categories.Export.class     | 1     |
| com.example.core.categories.Import.class     | 1     |
| com.example.core.categories.MemberData.class | 2     |
| com.example.core.categories.Priority1.class  | 2     |
| com.example.core.categories.Priority2.class  | 0     |
| com.example.core.categories.Priority3.class  | 0     |
Bsquare ℬℬ:

'Las pruebas realizadas por Categoría' dinámicos ordenador

(Método recomendado)

Probé una forma de realizar esto con un contador en la capa abstracta pero era dolorosa, tener que añadir el código fuente al principio de cada Métodos de ensayo.

Al final, este es el código fuente que escribí para responder a sus necesidades; es bastante pesado (reflexión ...), pero es la menos intrusiva con el código fuente existente, y responde totalmente a sus necesidades.

En primer lugar, se debe crear una Testsuite(que contienen varios otros Suites, o directamente todas las clases de prueba que desee), para asegurar al final, que todas las pruebas para las que desea estadísticas, se han cargado.

En esta suite, usted tiene que poner en práctica un "gancho final", llamada @AfterClassque se llama una vez por todas, cuando todo el conjunto de pruebas ha sido totalmente administrado por JUnit .

Esta el la implementación I Test Suite escribí para ti:

package misc.category;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.AfterClass;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;

@RunWith(Suite.class)
@Suite.SuiteClasses({ UnitTestWithCategory.class })
public class TestSuiteCountComputer {

    public static final String MAIN_TEST_PACKAGES = "misc.category";

    private static final Class<?>[] getClasses(final ClassLoader classLoader)
            throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
        Class<?> CL_class = classLoader.getClass();
        while (CL_class != java.lang.ClassLoader.class) {
            CL_class = CL_class.getSuperclass();
        }
        java.lang.reflect.Field ClassLoader_classes_field = CL_class.getDeclaredField("classes");
        ClassLoader_classes_field.setAccessible(true);
        Vector<?> classVector = (Vector<?>) ClassLoader_classes_field.get(classLoader);

        Class<?>[] classes = new Class[classVector.size()]; // Creates an array to avoid concurrent modification
                                                            // exception.
        return classVector.toArray(classes);
    }

    // Registers the information.
    private static final void registerTest(Map<String, AtomicInteger> testByCategoryMap, String category) {
        AtomicInteger count;
        if (testByCategoryMap.containsKey(category)) {
            count = testByCategoryMap.get(category);
        } else {
            count = new AtomicInteger(0);
            testByCategoryMap.put(category, count);
        }

        count.incrementAndGet();
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
        Map<String, AtomicInteger> testByCategoryMap = new HashMap<>();

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        while (classLoader != null) {
            for (Class<?> classToCheck : getClasses(classLoader)) {
                String packageName = classToCheck.getPackage() != null ? classToCheck.getPackage().getName() : "";
                if (!packageName.startsWith(MAIN_TEST_PACKAGES))
                    continue;

                // For each methods of the class.
                for (Method method : classToCheck.getDeclaredMethods()) {
                    Class<?>[] categoryClassToRegister = null;
                    boolean ignored = false;
                    for (Annotation annotation : method.getAnnotations()) {
                        if (annotation instanceof org.junit.experimental.categories.Category) {
                            categoryClassToRegister = ((org.junit.experimental.categories.Category) annotation).value();
                        } else if (annotation instanceof org.junit.Ignore) {
                            ignored = true;

                        } else {
                            // Ignore this annotation.
                            continue;
                        }
                    }

                    if (ignored) {
                        // If you want to compute count of ignored test.
                        registerTest(testByCategoryMap, "(Ignored Tests)");
                    } else if (categoryClassToRegister != null) {
                        for (Class<?> categoryClass : categoryClassToRegister) {
                            registerTest(testByCategoryMap, categoryClass.getCanonicalName());
                        }
                    }

                }

            }
            classLoader = classLoader.getParent();
        }

        System.out.println("\nFinal Statistics:");
        System.out.println("Count of Tests\t\tCategory");
        for (Entry<String, AtomicInteger> info : testByCategoryMap.entrySet()) {
            System.out.println("\t" + info.getValue() + "\t\t" + info.getKey());
        }

    }

}

Puede adaptarse a sus necesidades, en particular, la constante creé al principio, al paquete de filtro para tener en cuenta.

Entonces no tienes nada más que ver de lo que ya lo hacen.

Por ejemplo, esta es mi pequeña clase de prueba:

package misc.category;

import org.junit.Test;
import org.junit.experimental.categories.Category;

public class UnitTestWithCategory {

    @Category({CategoryA.class, CategoryB.class})
    @Test
    public final void Test() {
        System.out.println("In Test 1");
    }

    @Category(CategoryA.class)
    @Test
    public final void Test2() {
        System.out.println("In Test 2");
    }

}

En este caso, la salida es:

In Test 1
In Test 2

Final Statistics:
Count of Tests      Category
    1       misc.category.CategoryB
    2       misc.category.CategoryA

Y con el caso de prueba que contiene @Ignorela anotación:

package misc.category;

import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;

public class UnitTestWithCategory {

    @Category({CategoryA.class, CategoryB.class})
    @Test
    public final void Test() {
        System.out.println("In Test 1");
    }

    @Category(CategoryA.class)
    @Test
    public final void Test2() {
        System.out.println("In Test 2");
    }

    @Category(CategoryA.class)
    @Ignore
    @Test
    public final void Test3() {
        System.out.println("In Test 3");
    }   
}

Se obtiene la salida:

In Test 1
In Test 2

Final Statistics:
Count of Tests      Category
    1       (Ignored Tests)
    1       misc.category.CategoryB
    2       misc.category.CategoryA

Usted puede quitar fácilmente el "(Pruebas ignoran)" registro si lo desea, y por supuesto adaptar la salida como desee.

Lo que es muy agradable con esta versión final, es que se hará cargo de las clases de prueba que en realidad han sido cargados / ejecutadas, y por lo que tendrá un verdadero estadísticas de lo que se ha ejecutado, en lugar de una estadística estáticas como que tiene hasta ahora .

'Las pruebas realizadas por Categoría' estáticas ordenador

Si lo desea, como lo pediste, que no tienen nada que ver en el código fuente existente, esto es una forma de realizar las pruebas realizadas por Categoría de cómputo de forma estática.

Esta es la StaticTestWithCategoryCounterescribí para ti:

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;

public class StaticTestWithCategoryCounter {

    public static final String ROOT_DIR_TO_SCAN = "bin";
    public static final String MAIN_TEST_PACKAGES = "misc.category";

    private static final Class<?>[] getClasses(final ClassLoader classLoader)
            throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
        Class<?> CL_class = classLoader.getClass();
        while (CL_class != java.lang.ClassLoader.class) {
            CL_class = CL_class.getSuperclass();
        }
        java.lang.reflect.Field ClassLoader_classes_field = CL_class.getDeclaredField("classes");
        ClassLoader_classes_field.setAccessible(true);
        Vector<?> classVector = (Vector<?>) ClassLoader_classes_field.get(classLoader);

        Class<?>[] classes = new Class[classVector.size()]; // Creates an array to avoid concurrent modification
                                                            // exception.
        return classVector.toArray(classes);
    }

    // Registers the information.
    private static final void registerTest(Map<String, AtomicInteger> testByCategoryMap, String category) {
        AtomicInteger count;
        if (testByCategoryMap.containsKey(category)) {
            count = testByCategoryMap.get(category);
        } else {
            count = new AtomicInteger(0);
            testByCategoryMap.put(category, count);
        }

        count.incrementAndGet();
    }


    public static void computeCategoryCounters() throws Exception {
        Map<String, AtomicInteger> testByCategoryMap = new HashMap<>();

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        while (classLoader != null) {
            for (Class<?> classToCheck : getClasses(classLoader)) {
                String packageName = classToCheck.getPackage() != null ? classToCheck.getPackage().getName() : "";
                if (!packageName.startsWith(MAIN_TEST_PACKAGES))
                    continue;

                // For each methods of the class.
                for (Method method : classToCheck.getDeclaredMethods()) {
                    Class<?>[] categoryClassToRegister = null;
                    boolean ignored = false;
                    for (Annotation annotation : method.getAnnotations()) {
                        if (annotation instanceof org.junit.experimental.categories.Category) {
                            categoryClassToRegister = ((org.junit.experimental.categories.Category) annotation).value();
                        } else if (annotation instanceof org.junit.Ignore) {
                            ignored = true;

                        } else {
                            // Ignore this annotation.
                            continue;
                        }
                    }

                    if (ignored) {
                        // If you want to compute count of ignored test.
                        registerTest(testByCategoryMap, "(Ignored Tests)");
                    } else if (categoryClassToRegister != null) {
                        for (Class<?> categoryClass : categoryClassToRegister) {
                            registerTest(testByCategoryMap, categoryClass.getCanonicalName());
                        }
                    }

                }

            }
            classLoader = classLoader.getParent();
        }

        System.out.println("\nFinal Statistics:");
        System.out.println("Count of Tests\t\tCategory");
        for (Entry<String, AtomicInteger> info : testByCategoryMap.entrySet()) {
            System.out.println("\t" + info.getValue() + "\t\t" + info.getKey());
        }
    }

    public static List<String> listNameOfAvailableClasses(String rootDirectory, File directory, String packageName) throws ClassNotFoundException {
        List<String> classeNameList = new ArrayList<>();

        if (!directory.exists()) {
            return classeNameList;
        }

        File[] files = directory.listFiles();
        for (File file : files) {           

            if (file.isDirectory()) {
                if (file.getName().contains("."))
                    continue;

                classeNameList.addAll(listNameOfAvailableClasses(rootDirectory, file, packageName));
            } else if (file.getName().endsWith(".class")) {
                String qualifiedName = file.getPath().substring(rootDirectory.length() + 1);
                qualifiedName = qualifiedName.substring(0, qualifiedName.length() - 6).replaceAll(File.separator, ".");

                if (packageName ==null || qualifiedName.startsWith(packageName))
                    classeNameList.add(qualifiedName);
            }
        }

        return classeNameList;
    }

    public static List<Class<?>> loadAllAvailableClasses(String rootDirectory, String packageName) throws ClassNotFoundException {
        List<String> classeNameList = listNameOfAvailableClasses(rootDirectory, new File(rootDirectory), packageName);
        List<Class<?>> classes = new ArrayList<>();

        for (final String className: classeNameList) {
            classes.add(Class.forName(className));
        }

        return classes;
    }

    public static void main(String[] args) {
        try {           
            loadAllAvailableClasses(ROOT_DIR_TO_SCAN, MAIN_TEST_PACKAGES);
            computeCategoryCounters();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Sólo tiene que adaptar las dos constantes a partir de especificar:

  • donde son los (bytecode) clases
  • cuál es el paquete principal que es interesante (¿puedes configurarlo para que nullal considerar el 100% paquetes disponibles)

La idea de esta nueva versión:

  • una lista de todos los archivos que coinciden con sus clases 2 constantes
  • cargar todas las clases correspondientes
  • utilizar código fuente intacta de versión dinámica (ahora que las clases se han cargado)

Déjeme saber si usted necesita más información.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=146994&siteId=1
Recomendado
Clasificación