Code Coverage y Calidad de Código

Published on Author lopez

Voy a usar directamente la expresión en inglés “code coverage” para lo que en español es cubrimiento de código. La pregunta de hoy es ¿hay relación entre un gran nivel de code coverage y calidad de código?

Primero, el code coverage es una métrica: simplemente eso, ni más ni menos. Si conseguimos el porcentaje de code coverage luego, digamos, de ejecutar todos los tests del sistema, un alto porcentaje con todos los tests en verde sólo indica que al ejecutar los tests pasamos por una gran cantidad de líneas de nuestro código, sin provocar un error.

Pero cualquiera que se ponga a meditar sobre eso, no puede concluir así por que sí, que eso indique una buena calidad de código. Bien podemos imaginarnos código que cumpla con todo eso (buen métrica, pruebas que pasan) y que NO TENGA UNA BUENA CALIDAD de código. Por ejemplo, bien podemos refactorizar todo el sistema para que el código quepa en una clase, con cientos de métodos, y el code coverage seguirá alto y las pruebas pasarán. Y así se nos pueden seguir ocurriendo refactorizaciones, que bajen la calidad de código, sin que la métrica de code coverage se vea afectada en gran medida.

Tenemos que ponernos de acuerdo en qué es calidad de código, pero para este post, podemos tomar algunas cualidades:

– Que el código ejecute lo que se espera
‘- Que sea modificable/extensible de una forma accesible

Vean que no pongo que sea “entendible” porque me imagino que esa cualidad es un requisito de la segunda que puse arriba. La primera cualidad se puede dividir en:

– Que el código de el resultado esperado ante las entradas correctas
– Que el código reaccione de la manera esperada ante las entradas incorrectas

y así podríamos seguir refinando. Igual es tema para discutir. Pero aún no podemos esperar, con las cualidades de arriba u otras que se nos ocurran, que ante un gran nivel de code coverage obtengamos, de forma casi mágica, una buena calidad de código.

Entonces, si alguien espera que subiendo el code coverage de una aplicación/sistema/librería, escribiendo pruebas para subir esa métrica, de esa forma vamos a obtener/mejorar la calidad de código, les digo una cosa: es loable lo suyo, pero es tan inútil como alinear las sillas en la cubierta del Titanic. Están haciendo algo que parece bueno, pero no es lo que hay que hacer.

Alguien podría decir: ¿pero acaso las pruebas que acompañan al aumento de code coverage, no nos aseguran que el código inicial tenga la calidad esperada? Si hacen las pruebas DESPUES de escribir el código, muchas veces eso no se cumple. Porque se van a ver motivados a elevar la métrica, no la calidad del código. Pongamos un ejemplo en concreto. Sea un método que recibe los argumentos a y b, y revuelve a/b (su división). Al principio no está cubierta, agregamos una prueba que invoca a ese método pasando a=1, b=2 y vemos que devuelve 0.5. Ahora el code coverage subió, de 0 a 100%. Las pruebas pasan. Pero no tenemos idea de haber cubierto los (MINI)CASOS DE USO de ese método. Por ejemplo, nunca probamos que pasa cuando b=cero.

Ese es uno de los problemas de los “pruebas después”. No se cubren los casos de uso de nuestra aplicación. Y si encima nos dejamos guiar por aumentar el code coverage, menos vamos a concentrarnos en los casos de uso. Y si siguen el camino “pruebas después” también se encontrarán, más frecuentemente de lo necesario, que el código ya armado es difícil de probar, o lo van a modificar de formas extrañas para que podamos tener la recompensa del día: uy! aumentamos el code coverage! ¡Qué buenos programadores que somos, qué buena calidad de código!

No me malinterpreten: el code coverage sirve, pero NO ASEGURA calidad de código. Ni siquiera la mantenibilidad de un sistema. No es difícil encontrar sistemas con alto code coverage, uno los modifica, las pruebas pasan, pero luego, el sistema web vuela por los aires, rompiéndose, luego de dos clicks en las páginas. ¿Por qué? De nuevo, porque el code coverage alto y las “pruebas después” no aseguran para nada que hayamos contemplado todos los casos de uso (como el simple de más arriba, dividor por b igual a cero, el método tiene 100 por ciento de code coverage, pero no tiene el 100 por ciento de los casos de uso).

Algo de valor agrega el subir el code coverage, porque al final, alguna de las pruebas adicionales algo nuevo cubre. Pero es lo que alguien ha llamado “pica pica bajada de cordón (de vereda)”, trabajo que agrega poco valor en general a lo que programamos, y que lleva tiempo.

Bueno, luego de esta larga diatriba, algo de agua para mi molino:

Si en vez de intentar subir el code coverage con pruebas después, seguimos el FLUJO DE TRABAJO de Test-Driven Development, el code coverage alto es como que viene de la mano de ese flujo de trabajo, sin gran esfuerzo. Si aplicamos ese flujo a cubrir cada caso de uso, vamos construyendo software que va creciendo, como un organismo, paso a paso, de la manera más simple, y asegurándonos que cada prueba que agregamos responde a una necesidad del sistema que estamos armando. El código va fluyendo, naciendo, transformándose, y la etapa de refactor es de importancia para aumentar la calidad, no para el code coverage.

Espero que se haya entendido: sin TDD, no hay paraíso 🙂

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez