2012/09/01

Automatización de test - eci 2012

Resumen práctico del curso "Generación Automática de Tests Unitarios" (ECI 2012 N2) a cargo de Nazareno Aguirre, Universidad Nacional de Río Cuarto, Argentina


Esta reseña es solo un disparador, no es una guía ni una explicación detallada, es el resultado de hacer la práctica y el examen. Omito cosas importantes que supongo conocidas, pongo el foco en detalles secundarios y mecho con mis opiniones. Aclaro que mi relación con java es meramente académica, carezco de experiencia profesional. Sí tengo experiencia en unit testing, TDD y desarrollo en general.

Lo que se vió en el curso fue diversas maneras de automatizar el testing, ya sea generando tests, generando entrada para los tests o probando la resistencia de los tests.

Mi stack fue:
windowslinux
oracle java 1.7 (ups! la del 0day exploit!!openjdk con JavaSE-1.6 como execution environment para que funcione randoop
eclipse 3.7.2 indigo for java developerseclipse 4.2? juno for java developers
codecover 1.0.1.2
randoop 0.1.0.201107281327
jumble 1.0.0
korat 1.0
korattester 0.0.5 (FAIL)


CodeCover


Herramienta de análisis de cobertura de código.

La idea es que diversos aspectos de un programa como cada linea, cada decisión y cada camino posible sean recorridos por los test. Esta herramienta mide justamente eso.

Hay que habilitar para el proyecto (Propiedades->CodeCover->Enable Code Cover) y seleccionar criterios, luego seleccionar una clase a analizar (right click -> "Use for code coverage measurement") y finalmente ejecutar un test como "CodeCover measurement for junit".

Si hay distintos test cases se pueden agrupar en una suite o ejecutar cada uno separado y luego activar ambos resultados. Al activar (o como se llame) cada resultado, en el código fuente se ve la cobertura o su falta.

Instalación


via repo

Randoop


Herramienta de generación test al azar con feedback.

Para cada método, intenta generar código tal que se pueda llamar a ese método y dependiendo de los resultados, fallas y excepciones declaradas y lanzadas, detecta situaciones de falla y genera código de test para regresiones. Requiere que cada clase tenga un método repOk() que comprueba que el objeto esté en un estado válido. No es necesario que este método haga una validación completa.

Un ejemplo de repOK() para una pila

@CheckRep
public boolean repOk() {
    if (numItems < 0 ) return false;
    if (numItems == 0 && tope != null) return false;
    return true;
}


Uno mas completo que comprueba que el número de items corresponda a la realidad y que no sea una lista con un loop:

@CheckRep
public boolean repOK() {
    int cantidad = numItems;
    Nodo corr = tope;
    while (cantidad>0 && corr!=null) {
        corr = corr.obtenerSiguiente();
        cantidad--;
    }
    return (cantidad==0 && corr==null);   
}



Un ejemplo del código de test generado:

public void test49() throws Throwable {
    myUtils.PilaSobreListasEnlazadas var0 = 

        new myUtils.PilaSobreListasEnlazadas();
    var0.apilar((java.lang.Object)'#');
    java.lang.Object var3 = var0.tope();
    var0.desapilar();
    boolean var5 = var0.esVacia();
    var0.apilar((java.lang.Object)0L);
   
    // Regression assertion (captures the current behavior of the code)
    assertTrue("'" + var3 + "' != '" + '#'+ "'", var3.equals('#'));
   
    // Regression assertion (captures the current behavior of the code)
    assertTrue(var5 == true);
}


Hay que elegir una clase, right click -> Run as Randoop test input, poner un criterio de stop, algunos maximums y ejecutar. En el package/name provisto se generarán los test.


Tiene varias utilidades:

  • Teniendo tests, ver si no hay algún errorcito extra por ahi.
  • A partir de una implementación correcta pero ineficiente, tener una referencia para optimizarla.
  • A partir de una implementación ajena, tener una referencia para reimplementarla. ¡Ups!, ¿no será ilegal?
  • Testear código legacy, aunque con algunas restricciones. Si este código legacy no tiene tests, por algo será, ¿no? Probablemente tenga interdependencias y partes no mockeables que pueden imposibilitar la ejecución. Nada mejor que correr secuencias interminables de código al azar sobre un sistema en producción conectado a una base de datos.


Instalación


Bajar el jar y ponerlo en plugins

Jumble


Herramienta de code mutation.

Lo que hace es dada una clase y su testcase, cambiar en la clase algunas cosas como operadores aritméticos y lógicos y ejecutar el test. Si no falla, considera que está mal el test.

Sólo hay que elegir una clase, right click y "jumble class".

Al usar Parametrized Test Classes, me encontré con un problema. Como resuelve la clase de test por convención de nombres, hay que poner en un solo testcase los test parametrizados y los no parametrizados, lo que produce una ejecución de los tests no parametrizados repetida tantas veces como los parámetros, totalmente innecesaria. Hay una anotación, @TestClass, pero no logré que la respete ni apuntando a uno y otro test ni a una test suite con ambos.

Instalación


bajar el zip y copiar

cp -r jumble_1_1_0/eclipseplugin/plugin-export/jumble eclipse/plugins/

Korat


Herramienta para generar datos de entrada para test exhaustivos acotados.

Supongamos que queremos probar algún método con arrays de 4 posiciones con 4 valores. Se pueden generar todas las combinaciones sin problemas. Ahora bien, si la estructura es más compleja, como un árbol, la cosa se complica pues ya no sólo hay que generar los datos si no la estructura, lo cual produce varios inconvenientes:

  • muchas combinaciones
  • resultados redundantes (si tienen la misma forma, ¿que me importan los nodos en si?)
  • hay estructuras que pueden ser inválidas (loops, desconexiones)
  • pueden haber otros constraints, como que los datos estén ordenados en un árbol binario de búsqueda.

Korat, mediante una invariante y una breve descripción, puede generar estructuras válidas, no redundantes.



La mejor (única) manera que encontré de usar korat es bajar el snapshot del svn y agregar a examples la clase a analizar, luego se llama como en los ejemplo provistos, pero usando nuestra clase.

java -cp korat.jar korat.Korat --class korat.examples.colas.ColaSobreListasEnlazadas --args 4,0,1 --visualize

Los ejemplos son muy claros para estructuras, pero no hallé la manera de poblar los nodos de las estructuras cuando estos usaban objetos genéricos.

En eclipse he agregado korat.jar al build path para autocompletar con todo éxito.

Intenté usar un plugin de korat para eclipse, korattester, pero no funciona.

Instalación


svn co https://korat.svn.sourceforge.net/svnroot/korat/trunk/ korat


Pex


Este es el que menos pude investigar, ya que es para .NET.

No tengo nada que decir, sólo que pex4fun es muy divertido, si sos nerd.

Referencias


[eci n2] http://www.dc.uba.ar/events/eci/2012/cursos/aguirre

[docente]  http://dc.exa.unrc.edu.ar/staff/naguirre/Pagina_personal_de_Nazareno_Aguirre/Principal.html

[practica] https://sites.google.com/site/genunittests/


[java] http://www.oracle.com/technetwork/java/javase/downloads/index.html

[eclipse] http://www.eclipse.org

[codecover] http://codecover.org/

[codecover eclipse] http://update.codecover.org/

[randoop] http://code.google.com/p/randoop/

[randoop eclipse] http://randoop.googlecode.com/hg/plugin.updateSite/

[jumble] http://jumble.sourceforge.net/

[korat] http://korat.sourceforge.net/

[korattester] http://code.google.com/p/korattester/

[pex] http://research.microsoft.com/en-us/projects/pex/

[pex for fun] http://www.pexforfun.com

[pex standalone download] http://research.microsoft.com/en-us/downloads/d2279651-851f-4d7a-bf05-16fd7eb26559/default.aspx

1 comentario: