¿Porqué gestionar la calidad del código?
Es muy importante cultivar la calidad del código en un proyecto, desde etapas tempranas de su desarrollo por 2 importantes motivos:
1. Estar continuamente realizando revisiones en el código que se está generando, nos permite detectar oportunidades de mejora, reconocer posibles malas decisiones estructurales antes de incursionar en ellas y mantener el código legible, lo cual lo hace más fácil de comprender y facilita la tarea de mantenimiento.
Detectar riesgos de malas decisiones estructurales en etapas tempranas, nos permite corregir el camino que se va a tomar, antes de necesitar un volumen de re trabajo que puede llegar a ser inviable.
También evitamos que con el pasar del tiempo, queden algoritmos, clases o incluso paquetes en completo desuso, siendo “tierra de nadie”, motivo por el cual, también complica las tareas de mantenimiento.
2. El segundo motivo es, que mediante la plataforma SonarQube, nosotros generamos un legajo de documentación donde queda asentado con muchas métricas, como fue nuestro proceso de gestión de calidad en el código, durante la evolución del proyecto.
Este legajo puede oficiar de documentación defensiva para el equipo de desarrollo.

JUnit
JUnit es el framework de java que permite realizar pruebas automatizadas, nosotros debemos para cada método del proyecto, generar un contexto de datos de entrada, establecer un retorno esperado y eso será validado en cada ejecución.
También existe una herramienta muy recomendable como el framework Mockito, que nos permite generar simulaciones de comportamientos en los métodos del proyecto, lo cual nos facilita la creación de casos de test, pero en este articulo no voy a profundizar sobre el.
A continuación les presento una clase de test de ejemplo, el cual fue desarrollado para un proyecto anterior:
class UtilsTest {
MessageConverter messageConverter;
Utils utils;
@BeforeEach
void setUp() {
messageConverter = new MessageConverter();
utils = new Utils();
}
@Test
void toJson_Success_Test() throws IOException {
JsonTestObject testObject = new JsonTestObject("testName", 25);
String json = messageConverter.toJson(testObject);
assertNotNull(json);
assertTrue(json.contains("\"name\":\"testName\""));
assertTrue(json.contains("\"age\":25"));
}
@Test
void fromJson_Success_Test() throws IOException {
String json = "{\"name\":\"testName\",\"age\":25}";
JsonTestObject testObject = messageConverter.fromJson(json, JsonTestObject.class);
assertNotNull(testObject);
assertEquals("testName", testObject.getName());
assertEquals(25, testObject.getAge());
}
@Test
void fromJson_ThrowsException_Test() {
String invalidJson = "{name:testName,age:25}";
assertThrows(IOException.class, () -> {
messageConverter.fromJson(invalidJson, JsonTestObject.class);
});
}
@Test
void calculateDelay_deberiaCalcularRetrasoCorrectamente_Test() {
long baseDelay = 1000;
int retryCount = 3;
long expectedDelay = 8000;
long result = Utils.calculateDelay(baseDelay, retryCount);
assertEquals(expectedDelay, result);
}
}

Jacoco
Hace algún tiempo, realizábamos pruebas unitarias o de integración, pero no disponíamos de ninguna herramienta que nos genere algún reporte sobre la cobertura de nuestros test, para saber con certeza, que fragmentos del código quizá nunca pasaron por nuestras pruebas automatizadas.
Jacoco es una de las opciones que podemos usar para generar reportes de cobertura, es básicamente un plugin de maven, que si lo agregamos al proyecto, al ejecutar ./mvnw clean test -s settings.xml , se compilará el proyecto, se hacen las pruebas a nivel local y en el target del proyecto se generará una página estática, navegable, en la que podemos ver porcentajes de cobertura.
Lo que veremos a continuación es un reporte de cobertura hecho sobre un API de cálculo de la secuencia de fibonacci que desarrollé previamente.



La ruta por defecto de los reportes es target/jacoco-report/index.html
Al llegar a una clase vemos marcado en verde el código cubierto por las pruebas, y el que no en rojo.
El comando a ejecutar para realizar las pruebas y tener el reporte de cobertura es:
./mvnw clean test -s settings.xml
Para integrar jacoco a nuestro proyecto debemos agregar este plugin al pom.xml (es importante verificar la versión del mismo, que sea compatible con nuestra versión de java a usar en el proyecto):
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.11</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>report-aggregate</id>
<phase>verify</phase>
<goals>
<goal>report-aggregate</goal>
</goals>
<configuration>
<outputDirectory>${project.reporting.outputDirectory}/jacoco-aggregate</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>

SonarQube es una plataforma que nos permite ir generando métricas acerca del proyecto en la medida que va evolucionando, dicha información generada se guarda en un histórico clasificado por versión, branch y fecha. Las métricas están enfocadas en ayudarnos a gestionar la calidad del código.
Esta plataforma tiene un conjunto de reglas pre-configuradas, que se pueden modificar, en las que se basa SonarQube para darnos alertas, y establecer el tiempo de deuda técnica que tenemos para tener el proyecto al día.
En esta captura de pantalla, vemos el dashboard de sonarqube estando dentro de uno de los proyectos.

Podemos elegir el branch de gitlab donde vamos a trabajar, y visualizamos que por una
regla que está configurada, el Status del proyecto es failed (esta regla es que se requiere un 80% de cobertura en las pruebas).
Algunas de las métricas visualizadas son Bugs identificados, Vulnerabilidades de seguridad, Security Hotspots(posibles vulnerabilidades de seguridad no confirmadas, que se deben revisar) , deuda técnica en tiempo (que va bajando en la medida que nos vamos poniendo al día con las alertas), también nos habla de porcentaje de código duplicado y cantidad de bloques que se repiten a lo largo del proyecto.
Tenemos algo llamado “Code Smells”, que son “malos olores del código”, pueden ser desde imports y variables no usadas hasta excepciones mal gestionadas. Vienen agrupados por cada clase java que fue analizada, algunos de ellos se ven a continuación:

El porcentaje de cobertura del código y cantidad de test son información que es consumida del proyecto desde un XML generado por Jacoco.
Tenemos la posibilidad de visualizar gráficas que nos muestran históricamente como se fue desarrollando el proyecto, clasificado por versiones, e incluso podemos navegar en Sonarqube el código visualizándolo de manera muy similar a los reportes de Jacoco, viendo que lineas están cubiertas, e incluso que desarrollador escribió cada linea de código.

Integración de nuevos proyetos Gitlab con Sonarqube
Para generar un proyecto de gitlab en sonarqube, y poder trabajar con el debemos seguir los siguientes pasos:

Primer paso
Es importante aclarar, que es necesario hacer un proceso de integración entre sonarqube y la herramienta de versionado que usamos, dependiendo de cual sea, aquí disponemos de documentación brindada por Sonarqube
El primer paso entonces, dentro de sonarqube, es crear un proyecto usando la integración con gitlab (o la herramienta de versionado que estemos usando).

Nos aparecerá una lista de proyectos a seleccionar:

Una vez creado el proyecto, de esta pantalla debemos tomar el project key, en este caso sera “APITest”

Segundo paso
Las variables de entorno a agregar son las siguientes (hay que ejecutarlas siempre al iniciar el equipo)
export SONAR_ENABLED=1
export SONAR_PROJECT_KEY=APITest
export USUARIO_BD=usrdesa
export PASS_BD=usrdesa1
export URL_BD=db2://urldb:50000/SONARBD
export SONAR_PASSWORD=password
Estas variables de entorno, deben estar ajustadas a la base de datos que desees usar para respaldar todos los datos que guarda sonarqube sobre el proyecto, es importante tener un esquema de base de datos dedicado para cada proyecto que vayamos a analizar con esta herramienta.
Tercer paso
Debemos agregar las siguientes properties en el pom.xml del proyecto
<!--Sonarqube properties-->
<sonar.projectKey>${env.SONAR_PROJECT_KEY}</sonar.projectKey>
<sonar.projectName>APITest</sonar.projectName>
<sonar.host.url>https://host-sonarqube.com</sonar.host.url>
<sonar.login>${env.SONAR_USER}</sonar.login>
<sonar.password>${env.SONAR_PASSWORD}</sonar.password>
<sonar.branch.name>wip_nbr
</sonar.branch.name>
<sonar.coverage.jacoco.xmlReportPaths>${project.basedir}/target/jacoco-report/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.language>java</sonar.language>
<sonar.qualitygate.wait>true</sonar.qualitygate.wait>
Importante es que el projectKey sea el indicado, y el nombre del proyecto también.
Cada vez que deseemos cambiar de branch, debemos cambiar la sonar.branch.name
Cuarto paso
Asignar en el pom el branch a usar, que debe ser el mismo en el que debemos estar parado con git.
Ejecutar ./mvnw clean verify sonar:sonar
Con esto visualizaremos los primeros cambios en las métricas del proyecto creado.
Configurar exclusiones a la cobertura de Sonarqube
Como tip extra al post, voy a agregar la sintaxis para configurar exclusiones, para que sonarqube a la hora de realizar análisis no tome en cuenta ciertas clases o packages de java del proyecto.
Es importante tener esto bien ajustado, para que las métricas de nuestro proyecto no estén contaminadas.
¿En que situaciones debemos hacer esto?
- Clases de dominio sin lógica, que solo contienen getters, setters, constructores
- Clases que pertenecen a otros sistemas, como DTOs, que simplemente las tenemos por un tema de compatibilidad con sistemas externos.
- Clases vacías, o que van a ser retiradas próximamente del proyecto
- También nos puede interesar retirar de las alertas a todo el package de test, para que no se vean contaminadas las lecturas de codesmells (entre otros indicadores)
La sintaxis necesaria debe ser implementada a nivel del pom, y son propiedades propiamente dichas de Sonarqube, intenté por medio de Jacoco hacer configuraciones de exclusiones para cobertura (siguiendo al pie de la letra la documentación del plugin), y fue imposible de hacerlo funcionar.
<!--Exclusiones sonarqube-->
<sonar.sources>src/main/java</sonar.sources>
<sonar.tests>src/test/java</sonar.tests>
<sonar.exclusions>**/uy/com/filenotfound/dto**</sonar.exclusions>
<sonar.coverage.exclusions>**/uy/com/filenotfound/dto**</sonar.coverage.exclusions>
<sonar.test.exclusions>**/src/test/**</sonar.test.exclusions>
En ese fragmento de código, que debe estar dentro de <properties> en el pom, observamos que el package uy.com.filenotfound.dto , es excluido de todas las metricas de sonarqube, y a su vez de la cobertura de jacoco.
También es excluido todo el package de test, porque nuestro foco principal estará en el proyecto en si, para las métricas