Haciendo pruebas

| Publicado: | Código fuente

(Note: There's an English version of this report `here <http://www.taniquetil.com.ar/facundo/testing.txt>`_)

Mandé este mail originalmente a la lista interna de Canonical donde discutimos temas técnicos:

"Testing" (o testeo, o realizar pruebas) es un terreno resbaladizo en el desarrollo de software. Es como dejar de fumar: todo el mundo sabe que es algo bueno, pero pocos tienden naturalmente a hacerlo.

Además tiene facetas muy diferentes: pruebas de unidad, pruebas de integración, interfaces gráficas. Incluso en las interfaces gráficas, no es lo mismo hacer pruebas en PyGTK, o en interfaces web.

Por ejemplo, yo estoy convencido de que debemos hacer pruebas cuando desarrollamos software, que es una buena manera de evitar (o minimizar) deuda técnica, y que no sólo te mejora la calidad del producto, sino que tiene características más difíciles de medir (como innovación: nadie va a tocar un proyecto grande que no tiene pruebas sólo para probar algo nuevo).

Pero, todavía tengo algunas dudas, y mucho que aprender en este campo. ¿Es valioso en todos los casos? ¿Cual es el balance correcto entre pruebas de unidad y pruebas de integración? ¿Y qué hacemos con las interfaces gráficas?

Y un tópico probablemente controversial: yo sé que tenemos que hacer pruebas, pero gente en mi equipo no, ¡ayúdenme a convencerlos!

En cualquier caso, es algo para hablar. Podemos arrancar una charla acá, y luego agendar un par de reuniones para cubrir algunos temas particulares, una vez que sepamos cuales, o quizás encontrarnos en el UDS para discutirlo personalmente.

Estoy seguro que dentro de Canonical "Testing" se usa en todos lados, y podemos aprender unos de otros.

Ideas? Preocupaciones? Experiencias?

Esto generó un gran árbol de discusiones, con un montón de puntos en los que todos estaban de acuerdo, y un montón que fueron un poco controvertidos.

Entonces, luego de que el polvo se asentó, escribí un resumen de toda la conversación:

Esto es una especie de resumen con conclusiones y comentarios del hilo sobre Testing de los últimos días.

Es mejor mostrar ventajas sobre algo a la gente que forzarlos a usarlo. Hay un par de situaciones "fáciles de ver" donde las pruebas de unidad son claramente algo bueno. Jamu K. agregó algunos puntos a lo que yo mencioné en el correo original:

  • Te dicen cuando cometiste un error y rompiste algo. Cuanto antes detectes el error será más barato arreglarlo. Si un problema entra a un sistema en producción y afecta a los clientes, costará mucho en términos de satisfacción del usuario y tiempo para arreglarlo.
  • Son material educativo que ayuda un principiante (o alguien experimentado) a entender la lógica en una manera que no es posible simplemente al leer el código mismo. Esto es especialmente verdad cuando las pruebas ejercitan condiciones de error que no se desprenden de forma obvia del código.
  • Te ayudan a mantener una velocidad consistente. Es mucho menos probable que encuentres un problema que te haga perder dos días revisando y corrigiendo cuanto tenés buenas pruebas.
  • Te permiten optimizar la implementación con la confianza de que no rompiste ninguna API. "Primero hacelo bien, luego hacelo rápido" es bastante duro, y lo es aún más sin buenas pruebas.
  • Por definición, hacen que tu implementación se pueda probar. Te ayudan a entender cuando amontonaste demasiados detalles y te llevan a un mejor diseño.

Algunas ventajas son más conceptuales: muy claras a la gente que ya probó pruebas de unidad, pero no tan fáciles de ver para gente que realmente nunca lo hizo. Un ejemplo de esto es que diseñar para ser probado frecuentemente lleva a una mejor API (sin embargo, algunas veces lleva a una API más fea, porque estás forzado a agregar parámetros que sólo son útiles en un entorno de pruebas; como en todo, el equilibrio es el mejor de los consejos).

Una buena frase del hilo que me gustó como razón para hacer pruebas:

El código que estás escribiendo será usado por años. Será actualizado al cambiar los requerimientos. Y de vez en cuando, alguien que no es familiar al código como lo estás vos ahora estará en apuros para corregir un problema en él. ¿Qué previsión razonable podés hacer para ayudar a esa persona? Pensalo un minuto. Acordate, esa persona podrías ser vos.

¿Y qué desventajas hay? La gente a veces se queja de que si hacen pruebas, tardan más en escribir código. Sin embargo, lo que sucede es que las pruebas no cambian realmente el total de tiempo que toma desarrollar software, sólo cambia el cuando se paga ese costo. Sí, para software con pruebas, el beta inicial es mucho más completo y mejor probado, pero aparece más tarde en el ciclo, lo que puede ser un problema si no podés entregar ese código al usuario.

En todo el hilo, sólo se mencionó una desventaja real de TDD: puede pasar a veces que en lugar de realmente pensar profundamente un detalle del código, sólo lo vas toqueteando hasta que pasás las pruebas: esto puede llevar a código que esté menos pensado que lo que debería, ya que programás contra una luz verde, no contra un modelo mental limpio.

Hay que aclarar que no hay nada malo en no hacer TDD, pero que entrega resultados muy distintos con respecto a hacer las pruebas como corresponde, y se podría decir que al no hacer TDD los resultados son peores. ¿Está bien tener malos resultados? En algunos entornos, apuesto a que sí; sin embargo en Canonical queremos entregar lo mejor. Jamu lo dijo bien claro: si estás escribiendo código de producción sin hacer pruebas, entonces no estás haciendo tu trabajo correctamente. Mark S. lo reforzó con:

Deberíamos requerir pruebas para el código que somos responsables, y las excepciones que haya (seguro las habrán) necesitan justificarse y documentarse, no al revés.

Hacer pruebas es algo cultural, necesitamos encontrar cómo hacerlo crecer culturalmente: contratar gente que ya entienda y actúe con ese conocimiento, y entrenar a aquellos que todavía no tienen confianza en esto.

Hubo un montón de charla alrededor de hacer pruebas, pero nadie hizo distinción sobre que tipos de pruebas eran. Parece que las pruebas de unidad y de integración, o probar bibliotecas o interfaces gráficas, es todo lo mismo al discutir el tema.

Sin embargo, alguien preguntó específicamente sobre pruebas en interfaces gráficas. Realmente no sé sobre eso (¡quiero aprender!), pero creo que es un área menos conocida que las "pruebas sobre bibliotecas".

También se mencionó un tipo de pruebas que es raramente comentado fuera de los círculos de administradores de sistemas: cuando se maneja un servicio que se tienen que monitorear, es vital disponer de una manera de poder probar al servicio de manera frecuente y repetida tal que se pueda asegurar que sigue funcionando. Entonces, también se necesitan pruebas de reglas y procesos de negocios para los sistemas funcionado.

Hubo un poco de discusión acerca de las pruebas de documentación. "Doctests" son un buen material de aprendizaje para las bibliotecas, y pueden escribirse para mostrar funcionalidad y guiar por arriba a los usuarios. Pueden ser muy buenas para dar impresiones generales sobre el módulo.

Pruebas de documentación bien escritas son excelentes para documentar APIs, porque podés hacer que el conjunto de pruebas ejecute también aquellas, de manera de asegurarte que la documentación siempre permanece actualizada. Estás haciendo pruebas sobre la documentación, no usando la documentación para probar nada.

Quedó claro que las pruebas de documentación no reemplazan las pruebas de unidad, las complementan.

La discusión más grande del hilo fue sobre si TDD era útil en código experimental, o en etapas muy tempranas del desarrollo. Se aseguró que TDD funciona mucho mejor con códigos maduros que con código experimental. Esto se aplica también a características experimentales dentro de proyectos maduros. Básicamente se reduce a esto: si tenés una buena visión de lo que necesitás, escribir las pruebas primero te ayudan a señalizar el camino que vas a tomar. Si no tenés ni siquiera una buena idea de para qué dirección vas, TDD es un esfuerzo desperdiciado.

Esto generó algo de controversia, hasta que se explicó que "experimental" no es la mejor palabra para explicar que: estás en una fase de aprendizaje porque realmente estás tratando de entender mejor el problema. Una vez que entendiste el problema lo suficientemente bien como para tener una visión de la solución, volvés a TDD. Son realmente dos actividades distintas.

Esto normalmente sucede cuando la gente que escribe el código en modo "experimentación" sólo quiere ver si una idea loca va a funcionar o no, lo que muchas veces resulta en descubrir que todavía no se entiende el problema completamente.

Por otro lado, está la situación donde se necesita código en producción, y realmente no hay tiempo de hacer pruebas. Sí, ya sabemos, tendrá defectos, y a largo plazo es más caro, pero "lo necesitamos ya". Esto pasa en la vida real más veces que con las que estaría cómodo... Gustavo N. dijo algo que comparto completamente:

Si estás en una startup en una situación de vida o muerte (para la compañía), seguro... podés optar por ir realmente rápido, obtener un montón de mercado, y luego estabilizarte si resultó bien (mirá Twitter :-). Si sos parte de un contexto más grande (como nosotros), y tu producto no va a desaparecer pronto (ni la compañía que tiene una marca asociada con el producto), entonces estas rupturas pueden dañar realmente al producto y a la marca.

Entonces, como conclusión, por favor compartí sobre hacer pruebas en esta lista. Preocupaciones, ideas, tecnologías, si deberías o no hacer algo, etc.; este no es un tema donde todo es blanco o negro, o donde está todo dicho.

Si con el tiempo encontramos que es necesario una reunión para discutir algo (o incluso un grupo que se reúna regularmente), podemos ir a por ello. Mientras tanto, charlemos por acá.

Comentarios Imprimir