En el modelo de desarrollo tradicional (y quizá en nuestras mentes), el proceso de desarrollo más sensato es el siguiente:
- Obtenemos especificación de lo que tiene que hacer el proceso.
- Programamos lo que nos han pedido en base a la especificación.
- Probamos lo que hemos hecho, y si está bien, lo entregamos al cliente.
Esto es lo estándar, pero hay un par de cosas que hacen que no funcione bien del todo:
- Cuando los programadores testean, sólo prueban los cambios que han hecho, sin probar cosas que ellos consideran que no pueden fallar (y que, claro, es lo que suele fallar)
- Si un programador es el que hace el testeo, como está probando el código que acaba de escribir, tiende a pensar que su código está bien, así que no suele estar bien probado.
Para resolver esto, hace mucho tiempo que implantamos un proceso de “validación”. En este proceso, una vez terminada la programación, la incidencia pasa a otra persona, normalmente la misma que ha hecho la especificación, para que compruebe que lo que se ha hecho se corresponde con lo que se ha pedido. Otra vez, esto tiene dos motivos:
- Hay que comprobar que el programador ha hecho lo que estaba especificado: Hay veces que la especificación es incompleta o simplemente el programador lo ha entendido mal, así que hay que comprobar que lo que se recibe es lo que se pidió (algo así como comprobar el albarán de entrega de un pedido).
- Hay que comprobar que no hay fallos ocultos, cosas que el programador haya olvidado probar.
Esta labor de validación se hace dentro de la empresa. La gente de soporte, que es la que ha recibido la petición, se encarga de comprobar antes de devolver la incidencia al cliente, para así evitar la mayor cantidad de errores posibles.
Aún así, hay veces en los que errores se escapan, o modificaciones en el código afectan de forma inadvertida a otras partes del sistema en cuestión. Así, llegamos a la teoría del desarrollo basado en tests (o TDD, por sus siglas en inglés):
Se basa en cambiar el modelo básico que hemos descrito al principio: en lugar de programar y después probar, primero probamos (aunque parezca un contrasentido) y después programamos. Es decir:
Digamos que se nos pide una funcionalidad nueva, que nuestro sistema no tiene: por ejemplo, digamos que al matricular un alumno debe mandarse un correo electrónico a una dirección específica.
- Especificamos, en lugar de la funcionalidad de la tarea, la prueba o pruebas que dicha funcionalidad debe cumplir. Es decir, especificamos que si introducimos un nuevo alumno en la base de datos, la dirección de correo definida en una variable debe recibir dicho email.
- Programamos la prueba o pruebas: lógicamente, la primera vez que la ejecutemos una vez programada, fallará, porque la funcionalidad no está programada aún.
- Programamos la funcionalidad… nuestra especficación es el test: programamos para que el test pase correctamente.
Así, cada vez que programamos algo nuevo, tendremos unos poquitos tests que añadir a nuestro pool de tests. Este pool forma parte del proceso de integración continua: al recompilar, se ejecutan todos los tests y, si estos pasan, entonces se recompilan los procesos nuevos.
Se procede del mismo modo si se encuentra un error: se programa un test que falle al reproducir el error, y después programamos el cambio de funcionalidad que haga que se solucione el error. De esta forma, sabemos con certeza que este error en concreto no se va a volver a producir.
Es importante tener en cuenta que la programación basada en tests, según estudios de Microsoft, incrementa el tiempo total de desarrollo entre un 15 y un 35% del total, así que en algunas ocasiones, para optimizar el tiempo de desarrollo, sólo programamos tests para los procesos más críticos del sistema, y así reducir el tiempo total de desarrollo. Esto sólo se puede hacer en procesos simples, que no se ejecuten muy a menudo.
También hay que tener en cuenta otra consideración: en sistemas ya existentes, antes de la implantación de esta técnica, existen pocos tests. En estos sistemas integramos los tests poco a poco, según las peticiones de funcionalida de los clientes y sólo en las funciones más críticas, para no aumentar el número de horas trabajadas en exceso.