Este breve artículo explica cómo se puede lograr la cobertura del código utilizando el complemento JaCoCo para Maven. La idea detrás de las herramientas de cobertura de código, como JaCoCo, es averiguar qué partes del código fuente se han probado realizando un seguimiento de todas las líneas de código ejecutadas durante una prueba determinada.
Introducción
JaCoCo (Cobertura de código Java) ofrece cobertura tanto de línea como de sucursal. A diferencia de otras herramientas de cobertura de código (como Cobertura, por ejemplo), JaCoCo admite instrumentación de código sobre la marcha. Esto, lo hace ejecutando un agente Java que instrumenta y monitorea la ejecución del código. También se puede configurar para almacenar los datos recopilados de forma local en un archivo o de forma remota a través de TCP. Además, los archivos de datos de informes de varias ejecuciones se pueden combinar fácilmente. La última versión (> 0.6.4.201312101107) del complemento JaCoCo para Maven que es compatible con Java 7, proporciona dos nuevos objetivos, destinados a proporcionar cobertura de código para pruebas de integración también. Con estos objetivos de Maven, se pueden generar fácilmente dos informes de cobertura de código separados, uno para pruebas unitarias y otro para pruebas de integración. Es claramente deseable separar la cobertura del código de prueba de integración y unidad, principalmente porque ambas pruebas abordan diferentes aspectos de las pruebas del sistema. Mientras que las pruebas unitarias simples y simples abordan el problema de la corrección del código, las pruebas de integración abordan el problema de si una prueba del sistema de principio a fin es exitosa o no. Como tal, las pruebas de integración no se enfocan exclusivamente en los detalles de implementación de la clase, como por ejemplo, asegurarse de que se cubran todas las rutas de ejecución de las bifurcaciones, sino que se enfocan en el comportamiento del sistema como un todo. Por el contrario, las pruebas unitarias se enfocan solo en los detalles de implementación de la clase, donde generalmente se emplean afirmaciones o simulaciones para asegurarse de que se cubra cada ruta de ejecución de ramificación. Por lo tanto, se deduce que si se separan los informes de cobertura de prueba de integración y unidad, el código no probado se puede detectar fácilmente, ya que ahora se puede saber exactamente qué se está probando. Más importante aún, no hay riesgo de que una prueba de integración afecte inadvertidamente el resultado de cobertura general de una prueba unitaria, ya que los agujeros en una prueba unitaria son más fáciles de detectar. Si ese es el caso, la prueba unitaria en cuestión se puede reforzar en consecuencia, para cubrir aquellas secciones de código que solo estaban siendo objetivo de las pruebas de integración.Cobertura de código con Jenkins y Sonar
Jenkins es un servidor de integración continua que somete el código fuente de manera continua y repetida a ciclos de compilación en función de algunos criterios, como, por ejemplo, cuando detecta cambios en el SCM después de que se ha producido una confirmación. Sonar, por otro lado, es una plataforma de inspección continua, generalmente configurada de tal manera que monitorea constantemente la calidad del código procesando los resultados emitidos por una compilación de Jenkins para proporcionar métricas de código. Esto incluye cobertura de código, infracciones e informes de instancia de código duplicado. Por lo general, Sonar se configura como una acción posterior a la compilación de Jenkins, lo que significa que se ejecuta solo después de que Jenkins completa el proceso de compilación. Sin embargo, si lo desea, también se puede configurar para que se ejecute como un complemento de Maven, dentro del proceso de compilación en sí. Cuando se configura como una acción posterior a la compilación de Jenkins, los informes de cobertura de código se pueden enviar a Sonar de dos formas diferentes:- Usando su motor integrado para permitir que Sonar lance directamente la ejecución de la cobertura del código de prueba unitaria.
- Reutilizando informes existentes, previamente generados por herramientas externas como Maven y Ant.
Pruebas unitarias frente a pruebas de integración
Ya mencionamos que la última versión de JaCoCo proporciona objetivos especializados para cubrir las pruebas de integración, por separado de las pruebas unitarias. Del mismo modo, Maven distingue entre los dos y proporciona dos complementos diferentes para ejecutar pruebas de unidad e integración a través de los complementos Surefire y Failsafe, respectivamente. El complemento Surefire está diseñado para ejecutar pruebas unitarias y, de forma predeterminada, está vinculado a la fase de prueba del ciclo de vida de compilación de Maven. El complemento Failsafe, por otro lado, está diseñado para ejecutar pruebas de integración y debe usarse durante la prueba de integración y verificar las fases del ciclo de vida de la compilación de Maven. Ambos complementos generan informes de prueba, que detallan qué pruebas fueron exitosas, cuáles fallaron, el tiempo que tomó ejecutar un conjunto de pruebas y similares."Lo que hace que estos dos complementos aparentemente similares sean diferentes es lo siguiente. Si se utiliza el complemento Surefire para ejecutar pruebas de integración y se produce una falla en la prueba, la compilación se detendrá en la fase de prueba de integración, lo que provocará que la compilación falle por completo. Esto es claramente una desventaja, ya que durante una prueba de integración, los sistemas de los que depende una prueba determinada pueden quedar fuera de servicio, lo que hace que la compilación falle innecesariamente. El complemento Failsafe resuelve este problema al no fallar durante la fase de prueba de integración, lo que permite la publicación -Fase de prueba de integración a ejecutar.Con esto en mente, podemos decidir fácilmente cuándo usar uno y no el otro. Puede encontrar información más detallada sobre estos dos complementos en Maven Proyecto Surefire Testing Framework. En este artículo, se prefiere una configuración de complemento predeterminada estándar a una personalizada, ya que el POM resultante es más pequeño y menos desordenado. Esto significa que para Surefire, las pruebas que coincidan con estos patrones de comodines se incluirán automáticamente:
**/Test*.java
Incluye todos sus subdirectorios y todos los nombres de archivos java que comienzan con "Prueba".
**/*Test.java
Incluye todos sus subdirectorios y todos los nombres de archivos java que terminan con "Prueba".
**/*TestCase.java
Incluye todos sus subdirectorios y todos los nombres de archivos java que terminan con "TestCase".
Para Failsafe, se aplica lo siguiente:
**/IT*.java
Incluye todos sus subdirectorios y todos los nombres de archivos java que comienzan con "IT".
**/*IT.java
Incluye todos sus subdirectorios y todos los nombres de archivos java que terminan con "IT".
**/*ITCase.java
Incluye todos sus subdirectorios y todos los nombres de archivos java que terminan con "ITCase".
Surefire contra JaCoCo
¿Dónde entra JaCoCo y en qué se diferencia de Surefire? Lo que debe entenderse es que estos complementos de Maven interactúan con las clases de prueba de manera diferente. Si bien el trabajo de Surefire (o Failsafe) es ejecutar pruebas en una JVM, JaCoCo se engancha a esta JVM y, a través de su agente Java, puede instrumentar y monitorear. Esto significa que JaCoCo no puede funcionar por sí solo, sino que necesita instrumentar el código que ya está siendo ejecutado por un complemento como Surefire. Además, dado que JaCoCo está instrumentando código sobre la marcha, como se mencionó anteriormente, se dirige a las clases de Java y al código de bytes que contiene, donde la cobertura del código se calcula en términos de instrucciones de código de bytes de JVM, y no en líneas fuente. Solo cuando se genera el informe final de JaCoCo se consultan los archivos fuente reales de Java, de modo que se calculan las estadísticas de cobertura de línea adecuadas. Además de eso, para que el informe final de JaCoCo incluya números de línea y muestre el código fuente resaltado, la compilación debe realizarse con los símbolos de depuración habilitados; de lo contrario, la información de línea no estará disponible para el agente de JaCoCo. Cuando se ejecuta como un complemento de Maven, la configuración del agente JaCoCo se prepara invocando los objetivos de preparar-agente o preparar-agente-integración, antes de que se ejecuten las pruebas reales. Esto establece una propiedad denominada argLine que apunta al agente JaCoCo, que luego se pasa como un argumento JVM al ejecutor de pruebas. Una vez que el ejecutor de pruebas crea e inicia la JVM con argLine, la configuración del agente JaCoCo ya está en su lugar y la instrumentación del código se realiza de forma transparente. Tan pronto como finaliza el proceso de JVM, el agente de JaCoCo escribe las estadísticas de cobertura de código en un archivo. Según el objetivo de preparación del agente que se invocó, el archivo creado se denomina jacoco.exec para las pruebas unitarias y jacoco-it.exec para las pruebas de integración de forma predeterminada. Ambos archivos binarios se escriben en el directorio de destino. Los archivos de datos de cobertura de código de JaCoCo se pueden utilizar para generar un informe final, incluidas todas las estadísticas de ejecución, como la cobertura de instrucciones (código de bytes), la cobertura de rama y la cobertura de línea, junto con las líneas de código fuente real resaltadas en consecuencia. Estos informes fáciles de usar se pueden generar utilizando el informe y los objetivos de integración de informes, dependiendo de si se requieren informes de prueba de unidad o de prueba de integración. Los informes están disponibles en archivos HTML, XML y CSV.Preparación del POM para la cobertura del código
Ahora que se han introducido los complementos básicos de Maven, veremos cómo se puede configurar un POM típico para que ejecute pruebas unitarias y de integración y, al hacerlo, generar informes de cobertura de código separados para cada uno. En primer lugar, es necesario configurar el complemento JaCoCo.Configuración de JaCoCo
Como se explicó anteriormente, para que JaCoCo pueda instrumentar el código, el agente debe estar preparado antes de la ejecución de las pruebas. La configuración prepara dos agentes, uno para instrumentar pruebas unitarias y el otro para instrumentar pruebas de integración. Los objetivos de JaCoCo relacionados con la prueba de integración solo están disponibles en las versiones 0.6.4.201312101107 y superiores. Además de preparar estos agentes, también configuraremos el complemento JaCoCo para que los informes de cobertura de código se produzcan automáticamente durante la compilación de Maven. Estos informes se crean de forma predeterminada en el directorio target / site / jacoco. La configuración de este complemento se realiza de la siguiente manera: donde maven.jacoco.plugin.version es igual a 0.6.4.201312101107. El objetivo de preparar el agente utilizado para las pruebas unitarias está vinculado de forma predeterminada a la fase de inicialización del ciclo de vida, mientras que el objetivo de preparar el agente de integración está vinculado a la fase del ciclo de vida de la prueba previa a la integración. Ambos objetivos de generación de informes están vinculados a la fase de verificación del ciclo de vida.Configuración de Surefire y Failsafe
Una vez que la configuración de JaCoCo está en su lugar, los corredores de prueba se pueden configurar a continuación como se muestra a continuación: donde tanto maven.surefire.plugin.version como maven.failsafe.plugin.version son iguales a 2.8.1. El objetivo de prueba del complemento Surefire está vinculado de forma predeterminada a la fase del ciclo de vida de prueba, mientras que los objetivos de prueba de integración y verificación de los complementos Failsafe están vinculados a las fases de prueba de integración y verificación del ciclo de vida, respectivamente. Para resumir, enumeraremos las fases relevantes del ciclo de vida de Maven y lo que realmente ocurre en cada fase a medida que se ejecuta.- initialize: se establece la configuración del agente JaCoCo para las pruebas unitarias, con la propiedad argLine establecida en una configuración de agente similar a:
-javaagent:/path/to/local-maven-repo/org/jacoco/org.jacoco.agent/0.6.4.201312101107/org.jacoco.agent-0.6.4.201312101107-runtime.jar=destfile=/Workspace/path/to/maven-project/target/jacoco.exec
- compilar: ocurre la compilación del código fuente.
- prueba: el complemento Surefire lanza pruebas unitarias en JVM separadas, de acuerdo con los patrones de inclusión de prueba definidos "** / Test * .java", "** / * Test.java" y "** / * TestCase.java". Surefire aplica la propiedad argLine a cada JVM, que fue convenientemente configurada durante la fase de inicialización del ciclo de vida por JaCoCo. Tan pronto como la JVM termina, se escribe un archivo de datos JaCoCo jacoco.exec correspondiente, uno para cada módulo Maven.
- paquete: ocurre el empaquetado del código fuente compilado y los recursos relacionados.
- prueba previa a la integración: la configuración del agente JaCoCo para las pruebas de integración está configurada, con la propiedad argLine establecida en una configuración de agente similar a:
-javaagent:/path/to/local-maven-repo/org/jacoco/org.jacoco.agent/0.6.4.201312101107/org.jacoco.agent-0.6.4.201312101107-runtime.jar=destfile=/Workspace/path/to/maven-project/target/jacoco-it.exec
- prueba de integración: el complemento Failsafe lanza pruebas de integración en JVM separadas, de acuerdo con los patrones de inclusión de prueba definidos "** / IT * .java", "** / * IT.java" y "** / * ITCase.java" . Failsafe aplica la propiedad argLine a cada JVM, que fue convenientemente configurada durante la fase de ciclo de vida de prueba previa a la integración por JaCoCo. Tan pronto como la JVM termina, se escribe un archivo de datos JaCoCo jacoco-it.exec correspondiente, uno para cada módulo Maven.
- verificar: Se ejecutan el informe de JaCoCo y los objetivos de integración de informes, procesando los archivos de datos jacoco.exec y jacoco-it.exec respectivamente. El informe de cobertura de código para ambos se genera en el directorio de destino del proyecto.
Configuración de Sonar como acción posterior a la compilación de Jenkins
Anteriormente, vimos cómo el informe de JaCoCo y los objetivos de integración de informes se utilizan junto con jacoco.exec, jacoco-it.exec y los archivos fuente de la aplicación para producir un informe legible que detalla las estadísticas de cobertura del código. Los informes de JaCoCo no son más que una herramienta que puede procesar estos archivos de datos. Sonar es otra herramienta que puede analizar dichos archivos para generar un informe propio, siempre que esté configurado correctamente. En este artículo, Sonar está configurado en Jenkins como una acción posterior a la compilación. Por lo tanto, se asume que tanto Jenkins como su complemento Sonar están instalados correctamente. También se supone que el complemento Sonar Jenkins está configurado correctamente con una base de datos como MySQL, y que un servidor Sonar activo se refiere a esta base de datos. Para configurar el complemento Sonar Jenkins para procesar archivos de datos JaCoCo ya generados, se deben especificar las siguientes propiedades en el cuadro de texto "Propiedades adicionales", debajo de la instalación de Sonar que se está configurando:-
Configura Sonar para que se base en informes previamente generados externamente. En nuestro caso, estos informes (jacoco.exec y jacoco-it.exec) se generan durante la compilación de Maven que precede a la acción posterior a la compilación de Sonar.sonar.dynamicAnalysis=reuseReports
-
Omite el cálculo de métricas de diseño y dependencias.sonar.skipDesign=true
-
Establece el motor de cobertura de código que se utiliza para JaCoCo.sonar.java.coveragePlugin=jacoco
-
Establece la ubicación del archivo de informe JaCoCo de prueba unitaria.sonar.jacoco.reportPath=target/jacoco.exec
-
Establece la ubicación del archivo de informe JaCoCo de la prueba de integración.sonar.jacoco.itReportPath=target/jacoco-it.exec
-
Establece la ubicación del directorio de informes de Surefire. Failsafe no es compatible todavía, ya que estos no se presionan ni se muestran en Sonar.sonar.surefire.reportsPath=target/surefire-reports
- sonar.exclusions: excluye uno o más archivos o directorios del análisis de Sonar. Se admiten varias entradas separadas por comas.
- sonar.branch: establece una rama SCM con nombre. Si se nombran de manera diferente, dos ramas del mismo proyecto todavía se consideran proyectos de Sonar diferentes.
mvn clean install sonar:sonar -Plocalhost -Dsonar.host.url=http://localhost:9000 -Dsonar.jdbc.url=jdbc:mysql://localhost:3306/sonar -Dsonar.jdbc.username=root -Dsonar.jdbc.password=abcdefgh -Dsonar.dynamicAnalysis=reuseReports -Dsonar.skipDesign=true -Dsonar.java.coveragePlugin=jacoco -Dsonar.jacoco.reportPath=target/jacoco.exec -Dsonar.jacoco.itReportPath=target/jacoco-it.exec -Dsonar.surefire.reportsPath=target/surefire-reports