¿Por qué programar orientado a pruebas y convencer a tu equipo de utilizarlas?

Standard

Buen día, hoy quiero compartir un tema que desde hace relativamente poco tiempo he estado tratando de implementar en mi vida de programador, y esto es la implementación de la programación orientada a pruebas (Test Driven Development) en los proyectos de software.

tdd

Primero que nada me gustaría citar un texto que viene de la pàgina de RailsGuide y es esta: ´Testing support was woven into the Rails fabric from the beginning. It wasn’t an “oh! let’s bolt on support for running tests because they’re new and cool” epiphany.´ que podemos resumir como ´El soporte de pruebas en Rails fue pensado desde el principio y no un: “oh! vamos a agregar pruebas nada más porque son nuevas y están de moda”.´ el desarrollo a pruebas ha comprobado incrementar la satisfacción del cliente y la calidad del software, así que no hay que tomarlas tan a la ligera.

Lo anterior lo comento, porque muchas veces y me incluyo, nos gusta agregar cosas a nuestro proceso de desarrollo porque son la moda, y no porque realmente le vemos utilidad práctica y que nos vaya a ahorrar tiempo o ventajas a largo plazo.

Pero por lo menos desde mi punto de vista, la programación orientada a pruebas es un enfoque que tanto como desarrollador, cliente, jefe o cualquier rol involucrado en el desarrollo de software nos beneficia. Y trataré de evidenciarlo listando a groso modo el proceso que suelo seguir al implementar pruebas en mi código.

Antes de empezar recuerden, LAS PRUEBAS SE PROGRAMAN ANTES QUE EL CÓDIGO PARA PASARLAS” es decir, el primer código en tu aplicación deben ser las pruebas y el ciclo de tus pruebas es este.

 

TDDCycle

1.- Elegir un requisito/funcionalidad a probar.

2.- Establecer los criterios de aceptación para esa funcionalidad, es decir, qué necesita recibir y qué va a devolver para validar que la funcionalidad está completada.

3.- Programar las pruebas sin que existan las clases, métodos ni nada que se necesite.

4.- Correr las pruebas (Obviamente deberían de fallar porque no existen las clases, métodos, etc).

5.- Crear el código necesario para pasar las pruebas y correr las pruebas hasta que pasen.

6.- Una vez pasadas las pruebas, ver si podemos hacer refactor al código. En caso de no hacer refactor se da por terminado el proceso, de lo contrario se queda en el paso 5 y 6 hasta que ya no se hagan refactor.

Como podemos ver en la pequeña imagen que he colocado el ciclo debería de ser que las pruebas van a marcar error, después deberían marcar que han sido ejecutadas con éxito, hacer el refactor y seguramente vamos a provocar errores (no se preocupen, es normal) y al final reparar los errores y el proceso acaba una vez que ya todas las pruebas estén satisfechas y no se vaya a hacer refactor.

Pero, luego vienen las preguntas de muchos “¿Para qué gastar tanto tiempo haciendo esto en vez de ir y programar directamente el código?”, “¿Por qué no mejor las dejamos para el final?”, “¿Cómo quieres que programe pruebas si todavía no sé bien lo que voy a programar?”. Trataré de responderlas de la mejor manera que pueda pensar en este momento.

1.- “¿Para qué gastar tanto tiempo haciendo esto en vez de ir y programar directamente el código?”.

Primero que nada es diferente “gastar” a “invertir”, las pruebas no son un gasto, son una inversión, digamos por ejemplo que te integras a un proyecto, ¿cómo compruebas que el proyecto funcione?, seguramente lo corres en tu equipo y haces pruebas manuales, y también entenderás que esto no es para nada confiable, así que en primer lugar te vas a ahorrar las pruebas manuales.

Lo segundo, es ¿cómo verificas que tu código no afecte otras funcionalidades del sistema?, esto es algo que nunca pasa, modificamos un código y misteriosamente otra cosa deja de funcionar. Las pruebas nos ayudan con este detalle, ya que si después de modificar algo en el código, corremos las pruebas, estas nos van a decir si afectamos algo que no deberíamos.

Hay más ejemplos de este punto, pero creo que se entiende la idea.

2.- “¿Por qué no mejor las dejamos para el final?”.

Esta es un poco más difícil de explicar pero básicamente es por 2 razones, la primera es porque cuando programamos primero el código y después las pruebas, podemos “hacer trampa sin querer”, esto es que terminamos programando las pruebas para que nuestro código pase, en vez de que el código pase las pruebas, es decir, supongamos que tengo un método que me suma dos números, así que programo la prueba para que cuando le envío un 1 y un 2 yo espere un 3 de respuesta, pero ¿que tal si el código por la razón que queramos siempre devuelve 3? (notese que estoy exagerando el caso), pero como nuestra prueba funcionó probablemente asumamos que el código es correcto. Otro caso podría ser que también debimos considerar flotantes pero no se hizo y como nuestro código no lo contempla, puede que sólo programemos las pruebas que nuestro código contempla y no todo lo que debería.

La otra razón la explicaré en la siguiente pregunta ya que van de la mano.

3.- “¿Cómo quieres que programe pruebas si todavía no sé bien lo que voy a programar?”.

Bueno, como dije esta pregunta y la segunda pregunta van ligadas.

Primero les preguntaré, ¿por qué empiezas a programar si no sabes qué funcionalidad vas a programar?.

Lo que quiero que se entienda con esta pregunta es que si tu programación se enfoca en primero hacer las pruebas, por fuerza tienes que saber que vas a probar. Supongamos que un cliente les pide una calculadora. ¿Qué hace una calculadora?, suma, resta, multiplica y divide. Así que programamos eso, una calculadora que suma, resta, multiplica y divide. Pero cuando se la presentamos al cliente nos pide que le digamos dónde están las funcionalidades de calcular el módulo, el exponente y la raíz cuadrada.  Pero esto no es problema de pruebas si no de levantamiento de requerimientos. Así que continuemos, después de hablar con el cliente volvemos a “tirar código” así que agregamos las funcionalidades que nos solicitó, pero a mitad de nuestro código pensamos “de una vez debería programar la funcionalidad de cálculo de seno, coseno, logaritmo, etc. para que cuando lo pida, ya esté implementada la funcionalidad.” y aunque esto pueda parecer algo bueno en realidad es pésimo, ya que estamos agregando funcionalidad que el cliente nunca ha solicitado y si se la cobramos le estamos cobrando algo que no solicitó, y si no la cobramos estamos regalando código. Ahora, habrá quien diga “naaaa, eso nunca pasa”, pero pensemos cuántas veces hemos hecho validaciones de más o funcionalidades de más, así mismo cuántas veces no nos han faltado validaciones o alguna funcionalidad que resultó obvia para el cliente.

Las pruebas nos ayudan porque gracias a ellas vamos a definir nuestros requerimientos y por fuerza los criterios de aceptación, con los criterios de aceptación vamos a saber EXACTAMENTE lo que el cliente espera. Lo cual nos va, sin duda alguna ahorrar tiempo y dinero, e incluso líneas de código. Así mismo cuando el cliente pida funcionalidad extra, nuestros criterios de aceptación nos pueden ayudar a comprobar que entregamos lo que acordamos que íbamos a entregar.

 

Bueno, antes de extenderme más vamos a dejar el post hasta aquí, no me gustan los posts muy largos.

Sólo espero que con este pequeño artículo, a más de uno le quede la cosquilla de implementar en sus proyectos las pruebas y sobre todo que empiecen a ver las bondades del uso de pruebas.

Con esto voy a dar por terminado el tema del “¿por qué?”, quiero dejar claro que no hago justicia al tópico de pruebas, así que les recomiendo que le den una leída profunda al tema y si quieren ver algo de pruebas, haganlo con Rails, que la verdad estos tipos han hecho un trabajo ejemplar con el soporte a pruebas.

Por otro lado quiero escribir un poco de ¿cómo convencer a tu equipo?:

Esta no es tarea fácil, así que les diré lo que me ha tocado por experiencia.

La primera opción es que si tienes la autoridad para ordenarlo lo hagas ajhajahajh, esto es porque siempre habrá resistencia al cambio. Ahora, no me malinterpretes, esto no quiere decir que seas autoritario, haz labor de convencimiento con lo que menciono a continuación.

Una segunda opción (que siempre recomiendo) es que implementes las pruebas de manera individual, y después presentes los beneficios que has obtenido con esta implementación, ahora sí que “papelito habla”, es decir, no hay mejor evidencia que implementarlo y presentar los resultados.

Por último, podrías convencer con sólo palabras, o compartiendo artículos relacionados y tratar de que así despierte la curiosidad en el equipo y que ellos tomen la decisión de hacerlo.

Siempre ayudará además, compartir casos de otras empresas o equipos que han implementado pruebas y sus resultados.

Bueno, esto es todo por ahora, me gustaría más adelante hacer un artículo que vaya más a fondo en el tema por ahora les dejaré una lista que he recopilado y experimentado de beneficios que por tiempo no explicaré tan a detalle. Saludos y les deseo que implementen pruebas en su código.

 

  • Pasar menos tiempo en el debugger.
  • Localización de problemas más rápido.
  • Ver ¿cómo afecta tu cambio al resto de la aplicación?
  • Reducir el código (Ahora ya sabes específicamente que programar).
  • Reduce el acoplamiento.
  • Mejora la calidad.
  • Reduce el número de bugs.
  • Sirve para documentar los requerimientos.
  • Otras.

 

 

 

 

One thought on “¿Por qué programar orientado a pruebas y convencer a tu equipo de utilizarlas?

  1. Un punto muy importante para complementar el artículo de Jorge es que aparte de todos los beneficios que comenta, es que las pruebas y las suites de pruebas se pueden automatizar e integrar de manera continua con el resto del equipo.
    Esto que quiere decir, que si tienes una estrategia de integración con algún servidor como Jenkins o Bamboo, puedes incluirlas dentro de tu build automático, y cada vez que algún miembro del equipo realice un commit y el build se ejecute, tendrás una validación de que el cambio que se hizo cumple con tu diseño de pruebas inicial.

Leave a Reply

Your email address will not be published. Required fields are marked *