“Mi código funciona perfectamente porque hago pruebas unitarias”.
Esta es una de las frases que nos gustaría escuchar a todos cuando preguntamos por el control de calidad de un desarrollo software. Perfecto, tenemos pruebas unitarias que nos permiten prevenir de muchos errores en fases tempranas, pero… ¿nos estamos preocupando por la calidad del código de las pruebas unitarias?
Desarrolladores del mundo, os voy a revelar uno de los grandes misterios de la programación:
Las pruebas unitarias también son código fuente
Una de las características que define a una (buena) prueba unitaria es que debe ser tratada con la misma profesionalidad que se le trata al código fuente.
Entonces, ¿cómo puedo saber si estoy definiendo bien mis pruebas unitarias? No os preocupéis, para ello ya existen algunas técnicas como Mutation testing.
Ya, pero… ¿de qué va todo esto de las pruebas de mutación?
El concepto es muy simple: se trata de un tipo de pruebas que realizan pequeñas modificaciones en nuestro código fuente o byte code. A estos cambios se les conoce como mutantes. El objetivo es matar mutantes.
Cada mutante que sobrevive equivale a una prueba unitaria que no ha contemplado un caso que puede dar lugar a un error.
Los mutantes se crean empleando operadores de mutación, que imitan errores comunes de programación (cambios de signo, divisiones por cero, condiciones invertidas, etc.). La intención es ayudar a desarrollar pruebas efectivas buscando carencias en el conjunto de pruebas creadas.
Veamos un ejemplo. Si disponemos del siguiente código:
if (a && b) {
c = 1;
} else {
c = 0;
}
El operador de mutación condicional creará el siguiente mutante:
if (a || b) {
c = 1;
} else {
c = 0;
}
El código ha sido alterado de tal modo que ahora funciona justo al contrario de lo esperado. Así pues, cualquier prueba unitaria que ejecute ese código debería fallar. Si no fallase, algo hemos hecho mal y tendríamos un mutante superviviente.
Genial. Me encanta. ¿Cómo puedo empezar?
Si estamos desarrollando pruebas unitarias para código Java, disponemos de la herramienta PIT Mutation Testing.
Esta utilidad ofrece una gran lista de operadores de mutación para emplear en nuestras pruebas. Además, podemos ejecutarlo usando Maven, Ant o por línea de comandos. Tan sólo necesitamos disponer de un conjunto de pruebas unitarias previamente definidas en nuestro proyecto.
Seleccionamos un conjunto de operadores de mutación, le decimos dónde se encuentran los fuentes y los test del proyecto, y la herramienta nos genera un informe con los resultados de la ejecución de las pruebas sobre los mutantes. Nos calcula la cobertura de mutación, que no es más que una relación entre el número de mutantes derribados y supervivientes.
También dispone de un plugin para SonarQube, con el cual podemos detectar el número de mutantes supervivientes en forma de evidencias en el código.
En definitiva, sabemos la importancia de realizar pruebas unitarias, pero tenemos que cuidarlas de igual modo que lo hacemos con el código fuente. Para poder analizar posibles errores cometidos en nuestras propias pruebas, una de las diferentes opciones que tenemos son las pruebas de mutación. Existen herramientas que implementan este tipo de pruebas que son gratuitas y sencillas de utilizar.
Así que, ¡ya podemos presumir de hacer testing con garantías de calidad!
Artículo escrito por Jesús Badenas, desarrollador y consultor de calidad de software en excentia, una compañía que lucha por asegurar y gestionar la calidad de software de sus clientes, realizando auditorías de calidad, implantando herramientas, creando oficinas de calidad, formando a equipos y llevando a cabo pruebas de concepto. Partners de SonarSource, generando bienestar desde 2009.
Enhorabuena, es un gran artículo.
La primera vez que leí sobre mutation testing no entendí porqué todo el mundo se centra en la cobertura del código cuando esta “métrica” es más realista.
Una par de preguntas. ¿Qué valor utilizas como mínimo en un proyecto para asegurar la calidad? ¿Has llegado a valores altos con o sin TDD?
Hola!
Muchas gracias por tu comentario, de verdad. Respecto a tus preguntas:
– Si te refieres a la cobertura de código, considero que como mínimo se debería disponer de un 50% del código cubierto. Además, la ejecución de los test siempre debe acabar con éxito (100% éxito en los tests), si no la cobertura no sirve de nada.
Si hablamos de “cobertura de mutación”, la verdad es que es hago uso de ella desde no hace mucho y todavía no tengo la capacidad de decirte un número exacto para asegurar la calidad de las pruebas unitarias. Sin embargo, considero que se deberían revisar y “matar” todos los mutantes supervivientes que afecten al código crítico del proyecto.
– En cuanto al TDD, personalmente es una práctica que respeto al 100%, pero que no la sigo estrictamente. Dedico un esfuerzo por realizar y mejorar los test de mis desarrollos, pero sin centrarme en hacerlo antes o después. Eso sí… ¡Más vale tarde que nunca!