Beneficios y caraterísticas de un buen test unitario y de TDD
He leído un pequeño artículo sobre recomendaciones sobre Test-Driven Development: Guidelines for Test-Driven Development de Jeffrey Palermo. Me han llamado la atención sus apuntes sobre los beneficios de TDD y su descripción de un buen test unitario, así que las he traducido. Yo no soy muy partidario de TDD, pues me parece demasiado complejo el poder escribir los test antes, pero si soy muy partidario del testeo unitario.
Benefícios de Test-Driven Development
- El conjunto de test unitarios proporciona contante retroalimentación de que cada uno de los componentes sigue funcionando.
- Los test unitarios actuan como documentación que no se queda obsolet, al contrarío que otros tipos de documentación.
- Cuando el test pasa y el código de producción es refactorizado para eliminar duplicidades, es claro que el código está terminado, y el desarrollador se puede mover a la siguiente tarea.
- TDD fuerza un análisis y diseño crito porque el desarrollador no puede crear código de producción sin entender realmente cuales deberían ser los resultado deseados y como probarlos.
- El software tiende a estar mejor diseñado, esto es, menos acoplado y más facilmente mantenible, porque el desarrollador es libre de hacer decisiones de diseño y refactorizar en cualquier momento con la confianza de que el software todavia funciona.
- El conjunto de tests actua como una red de seguridad contra regresiones en los bugs: Si se encuentra un bug, el desarrollador debe crear un test que ponga de manifiesto el bug y despues modificar el código de producción para eliminar el bug. En sucesivas ejecuciones de los test, todas las correcciones de bugs son verificadas.
- El tiempo de depuración se reduce.
Características de un buen unit test
- Se ejecuta rápido, se ejecuta rápido, se ejecuta rápido. Si los test son lentos, no se ejecutaran a menudo.
- Separa o simula dependencias ambientales como bases de datos, sistemas de archivos, redes, colas y demás. Los test que ejercitan estos no serán rápidos y los fallos no dan información significativa sobre cual es el problema realmente.
- Es muy limitado en su alcance. Si el test falla, es obvio donde bucar el problema. Realiza pocas llamadas a la clase Assert de manera que el código que falla sea obvio. Es importante probar una única cosa en cada test.
- Se ejecuta y pasa de manera independiente. Si los tests requieren establecer entorno especial o fallan inexperadamente, no son buenos tests unitarios. Cambialos por simplicidad y fiabilidad. Los teste deben ejecutarse y pasarse en cualquier máquina. La excusa "funciona en mi máquina" no sirve.
- Usa a menudo stubs y mock objects. Si el código que está siendo probado llama a un base de datos o al sistema de archivos, estas dependencias deben ser simuladas. Esta dependencias habitualmente serán abstraidas usando interfaces.
- Revela claramente su intención. Otro desarrollador puede ver el test y comprender que se espera que haga el código de producción.