Creación: 18-10-2008
índice de contenidos
1. Introducción
2. Entorno
3. El código que queremos probar: Autentia.java
5. Configurando el pom.xml de Maven
7. Mejorando nuestro test, y viendo como funciona Cobertura
8. Forzar un mínimo de cobertura
9. Mejorando nuestro test (segunda parte)
1. Introducción
Cobertura (http://cobertura.sourceforge.net/) es una herramienta libre (GPL) escrita en Java, que nos permite comprobar el porcentaje de código al que accedemos desde los test. Es decir, Cobertura nos permite saber cuanto código estamos realmente probando con nuestros test.
De esta forma Cobertura se convierte en una potente herramienta de trabajo, ya que lo podemos usar como medida de calidad (mientras más código tengamos probado, más garantías tenemos de que podemos hacer refactorizaciones sin peligro).
Además Cobertura también nos indica la complejidad ciclomática de McCabe (http://en.wikipedia.org/wiki/Cyclomatic_complexity).
Esto nos dice como de «complejo» es un método. Esto nos puede servir para orientar nuestros test y probar primero las piezas más complejas, o incluso nos puede hacer plantearnos una refactorización para bajar la complejidad del código.
Como veis, muchas son las ventajas de usar herramientas de este estilo, así que en este tutorial vamos a ver como integrar Cobertura en nuestro ciclo de Maven 2 (http://maven.apache.org/), y aprenderemos a interpretar los sencillísimos informes que nos va a generar (son tan sencillos de interpretar, que se convierten en otro de los puntos fuertes de Cobertura).
2. Entorno
El tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil Asus G1 (Core 2 Duo a 2.1 GHz, 2048 MB RAM, 120 GB HD).
- Nvidia GEFORCE GO 7700
- Sistema Operativo: GNU / Linux, Debian (unstable), Kernel 2.6.26, KDE 3.5
- Java Sun 1.6.0_07
- Maven 2.0.9
- Cobertura 1.9
3. El código que queremos probar: Autentia.java
Vamos a poner un sencillo ejemplo, una sola clase con un sólo método:
package com.autentia.tutorial; public class Autentia { public void tellMeSomething(int i) { if (i < 5) { System.out.println("Soy menor que cinco"); return; } if (i % 2 == 0) { System.out.println("Soy un número par"); return; } } }
4. El test: AutentiaTest.java
Un pequeño test para probar un pequeño programa 😉
package com.autentia.tutorial; import org.junit.Test; public class AutentiaTest { @Test public void testApp() { final Autentia autentia = new Autentia(); } }
5. Configurando el pom.xml de Maven
En el
pom.xml
añadiremos las siguientes líneas para que se nos generen los informes de Cobertura:
... <reporting> <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> </plugin> </plugins> ... </reporting> ...
6. Nuestro primer informe
Los informes de Cobertura se generan dentro del «site» de Maven, así que tendremos que ejecutar:
$ mvn site
Si ahora nos vamos a la sección de informes veremos que tenemos uno llamado: «Cobertura Test Coverage». Le pinchamos y deberíamos ver algo similar a esto:
El informe es como si se tratara de una documentación de Javadoc, donde arriba a la izquierda tenemos los paquetes, justo debajo las clases (o todas, o las del paquete seleccionado), y a la derecha tenemos la información del elemento seleccionado (general, paquete, clase, …).
En la imagen se pude ver como el paquete com.autentia.tutorial
sólo está probado en un 12%.
Si pinchamos sobre la clase, veremos algo como:
En la zona de la derecha, arriba podemos ver una pequeña tabla resumen:
- Classes in this File: clases en este fichero, por si tenemos clases internas, que no es el caso.
-
Line Coverage: Cuantas líneas de este fichero se han probado. Nos dice que se ha probado 1 línea de 8 líneas ejecutables (para el Cobertura, las líneas ejecutables con la
3, 6, 7, 8, 11, 12, 13, 15, que son las que están marcadas en verde en la primera columna). - Branch Coverage: Nos indica cuanto posibles caminos (if else, while, for, …) se han probado. En el informe vemos que se han probado 0 caminos de 4 posibles.
- Complexity: La complejidad ciclomática McCabe de este fichero.
Justo debajo de esta tabla resumen no encontramos con el código fuente:
La columna de la izquierda es el número de línea (en verde si se trata de una línea ejecutable), la siguiente columna es el número de veces que la línea ha sido probada (las veces que se ha ejecutado esa línea). En la imagen podemos ver como por la línea 3 (la construcción de la clase) sólo se ha pasado 1 vez.
Por último podemos ver como en rojo tenemos marcadas todas las líneas que no han sido probadas.
La conclusión es que nuestro test no es demasiado bueno 🙁 pero gracias Cobertura nos hemos dado cuenta de ello, y por eso podemos mejorar 🙂
7. Mejorando nuestro test, y viendo como funciona Cobertura
Antes de hacer nada, vamos a explicar un poco como funciona Cobertura. Cobertura lo que hace es instrumentalizar nuestro código. Esto significa que, en tiempo de compilación lo «altera» para llevar un contador del número de veces que se pasa por cada línea. Esta información se va acumulando en un fichero llamado cobertura.ser
.
Y ojo porque hemos dicho «acumulando», esto quiere decir que siempre se añade nunca se borra. Esto es necesario para poder probar, por ejemplo, distintos módulos o distintos conjuntos de pruebas, pero esto implica que si ejecutamos dos veces el mismo test, el número de líneas ejecutadas se irá incrementando. Para evitar esto tenemos dos soluciones:
-
$ mvn clean
: borra toda la información de salida de Maven (un poco drástico) -
$ mvn cobertura
: clean borra sólo la información que almacena Cobertura, con lo que la siguiente vez que generemos los informes estos se harán a partir de un entorno «limpio».
NOTA: No debemos preocuparnos por la instrumentalización del código, ya que Cobertura la hace en un directorio diferente, por lo que nuestras clases, que luego se empaquetarán, quedan intactas.
Ahora nuestro test va a ser el siguiente:
package com.autentia.tutorial; import org.junit.Test; public class AutentiaTest { @Test public void testApp() { final Autentia autentia = new Autentia(); autentia.tellMeSomething(3); } }
Para lanzar otra vez el site, haremos:
$ mvn cobertura:clean site
Esta vez el resultado del informe será:
Se puede ver como hemos mejorado bastante: ya estamos probando el 50% del código, aunque todavía sólo un 25% de los caminos posibles.
8.Forzar un mínimo de cobertura
Hasta ahora sólo hemos estado usando Cobertura para sacar informes. Pero también podemos integrarlo en el ciclo de desarrollo, de forma que si nuestro código no está mínimamente cubierto por los test, no nos deje sacar una versión.
Para conseguir esto, en nuestro pom.xml
, además de la configuración anterior, añadiremos las siguientes líneas:
... <build> <plugins> ... <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <configuration> <check> <haltOnFailure>true</haltOnFailure> </check> </configuration> <executions> <execution> <goals> <goal>clean</goal> <goal>check</goal> </goals> </execution> </executions> </plugin> ... </plugins> </build> ...
Ojo, porque estas líneas las hemos puesto dentro del elemento build
, mientras que antes las habíamos puesto dentro del elemento reporting
.
Por defecto Cobertura va a exigir un mínimo de un 50% de líneas probadas y un mínimo de un 50% de caminos probados. Esto es configurable y podemos aumentar o disminuir el porcentaje. Incluso podemos configurarlo por paquete o incluso para una clase concreta. Para saber como hacer esto, podéis encontrar información en la documentación del plugin: http://www.mojohaus.org/cobertura-maven-plugin/usage.html
Si ahora ejecutamos:
$ mvn clean install
Veremos el siguiente error:
Cobertura: Loaded information on 1 classes. Cobertura: Saved information on 1 classes. [INFO] [cobertura:check {execution: default}] [INFO] Cobertura 1.9 - GNU GPL License (NO WARRANTY) - See COPYRIGHT file Cobertura: Loaded information on 1 classes. [ERROR] com.autentia.tutorial.Autentia failed check. Branch coverage rate of 25.0% is below 50.0% Package com.autentia.tutorial failed check. Package branch coverage rate of 25.0% is below 50.0% Project failed check. Total branch coverage rate of 25.0% is below 50.0%
Hasta que no tengamos suficientemente cubierto nuestro código, Maven no nos dejará continuar.
9. Mejorando nuestro test (segunda parte)
Añadimos una nueva línea a nuestro test:
package com.autentia.tutorial; import org.junit.Test; public class AutentiaTest { @Test public void testApp() { final Autentia autentia = new Autentia(); autentia.tellMeSomething(3); autentia.tellMeSomething(6); } }
Ahora si intentamos compilar no tendremos ningún problema. Y si generamos los informes obtendremos esto:
Se ve como estamos probando el 88% de las líneas y el 75% de los caminos posibles.
10. Conclusiones
No hay que buscar la calidad perfecta ni el 100% de cobertura, esto no es inteligente ni práctico, ya que nos llevaría demasiado tiempo y esfuerzo. Pero si es necesario unos mínimos de calidad y enfocar nuestros esfuerzos a probar las piezas más complicadas o más importantes para negocio.
Herramientas como Cobertura nos ayudan enormemente a conseguir estos objetivos y son un aliado fundamental para entornos de mejora continua.
Y recordad siempre que, sin medir, es imposible mejorar. Hay que medir antes y después, y comparar las medidas. Eso es lo que realmente me indica si estoy mejorando o empeorando.
<!–Aquí os dejo todo el proyecto de Maven para que podáis ver el código completo y podáis jugar con él.–!>
11. Sobre el autor
Alejandro Pérez García, Ingeniero en Informática (especialidad de Ingeniería del Software)
Socio fundador de Autentia (Formación, Consultoría, Desarrollo de sistemas transaccionales)
mailto:alejandropg@autentia.com
Autentia Real Business Solutions S.L. – «Soporte a Desarrollo»
Hola,
Siguiendo los pasos del manual de maven para cobertura no soy capaz de excluir una clase… Esta clase no se prueba con junit y querria quitarla de las estadisticas de cobertura. Tengo configurado esta parte del plugin de esta forma:
En la parte de build:
org.codehaus.mojo
cobertura-maven-plugin
2.3
UTF-8
clean
y en la parte de reporting:
org.codehaus.mojo
cobertura-maven-plugin
es.udc.bibliotecaEmblemas.web.ajax.*
com/uno/paraexcluir/*.class
He probado cambiando de sitio esta ultima configuracion y nada, cualquier ayuda la agradeceria!!
un saludo
Han pasado ya más de 4 años y por más pruebas que he hecho sigo sin dar con la solucion. Eso sí, he subido de versión a la 2.7 pero ni con esas. Cualquier ayuda será bienvenida!!
un saludo
Pues no sé qué decirte porque habría que ver está montado todo y además el articulo es de hace once años, así que puede que haya cosas que ahora sean diferentes.
En cualquier caso las herramientas de cobertura siguen vigentes:
– http://cobertura.github.io/cobertura/
– https://github.com/jacoco/jacoco
– http://emma.sourceforge.net/
– …