2010-07-18 9 views
11

Acabo de comprar The Art of Unit Testing de Amazon. Soy bastante serio sobre la comprensión de TDD, así que puede estar seguro de que esta es una pregunta genuina.Intentando ganar confianza en los beneficios de TDD

Pero siento que estoy constantemente a punto de encontrar la justificación para renunciar a ella.

Voy a jugar al abogado del diablo aquí y tratar de derribar los supuestos beneficios de TDD con la esperanza de que alguien pueda demostrar que estoy equivocado y ayudarme a tener más confianza en sus virtudes. Creo que me estoy perdiendo algo, pero no puedo entender qué.

1. TDD para reducir errores

Este often-cited blog post dice que las pruebas unitarias son herramientas de diseño y no para la captura de insectos:

En mi experiencia, las pruebas de unidad no son una manera eficaz de encuentra errores o detecta regresiones.

...

TDD es una forma sólida de diseñar componentes de software (“unidades”) forma interactiva por lo que su comportamiento se especifica a través de pruebas de unidad. ¡Eso es todo!

Tiene sentido. Los casos extremos siempre van a estar ahí, y solo encontrarás los errores superficiales, que son los que encontrarás tan pronto como ejecutes tu aplicación de todos modos. Aún debe realizar pruebas de integración adecuadas después de que haya terminado de construir una buena parte de su software.

Lo suficiente, la reducción de errores no es lo único que se supone que TDD ayuda.

2. TDD como un paradigma de diseño

Este es probablemente el más grande. TDD es un paradigma de diseño que lo ayuda (o le obliga) a hacer que su código sea más composable.

Pero la capacidad de compilación es una calidad de realización múltiple; el estilo de programación funcional, por ejemplo, hace que el código sea también composable. Por supuesto, es difícil escribir una aplicación a gran escala completamente en estilo funcional, pero existen ciertos patrones de compromiso que puede seguir para mantener la capacidad de compilación.

Si comienza con un diseño funcional altamente modular, y luego agrega cuidadosamente el estado y IO a su código según sea necesario, terminará con los mismos patrones que TDD alienta. Por ejemplo, para ejecutar la lógica de negocios en una base de datos, el código IO podría aislarse en una función que realiza las tareas "monádicas" de acceder a la base de datos y pasarlo como argumento a la función responsable de la lógica comercial . Esa sería la forma funcional de hacerlo.

Por supuesto, esto es un poco torpe, por lo tanto, en su lugar, podríamos arrojar un subconjunto del código IO de la base de datos en una clase y darlo a un objeto que contenga la lógica empresarial relevante. Es exactamente lo mismo, una adaptación de la forma funcional de hacer las cosas, y se conoce como el patrón de repositorio.

Sé que esto probablemente me gane una flagelación bastante mala, pero muchas veces, no puedo evitar sentir que TDD solo compensa algunos de los malos hábitos que la POO puede alentar, los que pueden ser evitado con un poco de inspiración del estilo funcional.

3. TDD como documentación

TDD se dice que sirven como documentación, pero sólo sirve como documentación para sus compañeros; el consumidor aún requiere documentación de texto.

Por supuesto, un método TDD podría servir como base para código de muestra, pero las pruebas generalmente contienen algún grado de simulaciones que no deberían estar en el código de muestra, y suelen ser bastante ingeniosas para que puedan evaluarse contra el resultado esperado.

Una buena prueba de unidad describirá en su método la firma del comportamiento exacto que se está verificando, y la prueba no verificará más ni menos que ese comportamiento.

Por lo tanto, yo diría que es mejor dedicar su tiempo a pulir su documentación. Diablos, ¿por qué no hacer solo la documentación primero y llamarlo Diseño orientado a la documentación?

4. TDD para pruebas de regresión

Es mencionado en ese puesto por encima de ese TDD no es demasiado útil para detectar regresiones. Eso es, por supuesto, porque los casos extremos no obvios son los que siempre se estropean cuando cambias algún código.

Lo que también podría tenerse en cuenta sobre ese tema es que hay muchas posibilidades de que más de su código permanezca igual durante bastante tiempo. Entonces, ¿no tendría más sentido escribir pruebas unitarias según sea necesario, siempre que se cambie el código, manteniendo el código anterior y comparando sus resultados con los de la nueva función?

+1

"que se pueden evitar con un poco de inspiración del estilo funcional". Casi estoy de acuerdo ... TDD fomenta un estilo de codificación más cercano al modelo Actor, que es una especie de inversión del estilo funcional (es un modelo push en comparación con el tirón de la codificación funcional) – kyoryu

+0

Interesante ... Debería ver el modelo Actor . ¡Eso podría contener la respuesta que estoy buscando! –

+0

Vale la pena señalar que Alan Kay ha sido citado diciendo que lamenta utilizar el término "orientado a objetos" y desearía haber usado "orientado a mensajes". TDD lo empuja hacia un estilo orientado a mensajes (que es el corazón del modelo actor, o la comunicación de procesos secuenciales, o ...) – kyoryu

Respuesta

3

Creo que el beneficio de TDD es que realmente escribes las pruebas, ya que son más interesantes cuando son un objetivo que tienes que lograr, (crear código para pasar las pruebas), en lugar de una tarea que tienes que hacer después .

Además, lo pone en la mente del usuario. Tienes que pensar "¿qué necesita el usuario que haga mi método?" O lo que sea, en lugar de "Espero que mi método haya logrado lo que se suponía que debía hacer". De esta manera, también puede ayudar a reducir errores.

+0

No estoy seguro de que esté convencido de que TDD no es exagerado para este propósito, pero esto parece ser la motivación más sensata para TDD. –

+0

Bueno, también puedo tener una lista de cosas por hacer para eso ... –

+0

Sí, esa es la cuestión, es por eso que después de casi un año todavía no estoy convencido. –

0

TDD también le proporciona un código que está completamente cubierto por las pruebas unitarias automáticas. Esto es simplemente porque no se escribe ningún código hasta que sea necesario realizar una prueba de unidad fallida.

1

sólo quería destacar:
pruebas TDD no son pruebas unitarias

"El propósito de una unidad de prueba es para probar una unidad de código de forma aislada."
"Una prueba TDD, a diferencia de una prueba unitaria, podría probar más de una sola unidad de código a la vez". Las pruebas TDD son más pruebas de interacción ....

de muy buena entrada en el blog de Stephen Walter TDD Tests are not Unit Tests

5

En términos de diseño, un importante beneficio de un TDD usted no está viendo es que impulsa el diseño a ser suficiente.Ya sabes lo que dicen que el ingeniero ve el cristal como el doble de grande que debería ser. El overdesign en el software puede ser un gran problema. Encuentro que el 90% del tiempo de TDD forzó la separación correcta de la abstracción para admitir la extensión posterior del código. TDD no es mágico, también necesita al programador para hacerlo, pero es una parte importante del juego de herramientas.

Creo que hay demasiados TDD aislados en su lista. ¿Qué hay de la refactorización? Creo que uno de los principales beneficios de la prueba es que bloquea el comportamiento, por lo que cuando se refactoriza puede estar seguro de que no ha cambiado nada, lo que a su vez puede hacer que confíe en la refactorización. Y no hay nada como un diseño que nace de la experiencia en lugar de una pizarra (aunque el diseño de la pizarra de alto nivel sigue siendo muy importante).

Además, escribe: "Entonces, ¿no tendría más sentido escribir pruebas unitarias según sea necesario, siempre que se cambie el código, manteniendo el código anterior y comparando sus resultados con los de la nueva función?" El código que se escribe sin pruebas de unidades en mente a menudo no puede ser comprobado, especialmente si interactúa con servicios externos tales como una base de datos, un administrador de transacciones, un kit de herramientas GUI o un servicio web. Agregarlos más tarde simplemente no va a suceder.

Creo que Bob Martin lo dijo mejor, TDD es programar lo que Double Entry es para contabilidad. Previene una cierta clase de errores. No evita todos los problemas, pero se asegura de que si su programa pretende agregar dos más dos, no los resta en su lugar. El comportamiento básico es importante, y cuando sale mal, puede pasar mucho tiempo conociendo su depurador.

+0

Hmm ... forzarle a escribir el código "solo suficiente" es en realidad un efecto de la capacidad de compilación forzada también. Comprender cosas como la transparencia referencial y los efectos secundarios obligará a tu código a hacer "lo suficiente" igual de bien. La refracción es a lo que me refiero en mi segundo punto, que hace que el código sea más composable. El software composable es, por definición, fácil de refractar. En la mayoría de los casos, con ciertas reglas en mente, puedo derivar patrones de OOP del estilo funcional que produce un código modular que tiende a verse y sentirse como un código TDD. –

+0

@Rei, si su argumento es que teóricamente es posible obtener un código similar a TDD sin TDD, nadie discute con usted (incluso los defensores del núcleo duro de TDD). Sin embargo, eso no lo hace probable o tan fácil. – Yishai

+2

Con respecto a la refactorización, no importa cuán composable sea su software, si desea refactorizar la estructura interna de su código, tendrá miedo de hacerlo sin una red segura. A menos que seas excepcionalmente bueno o inusualmente tonto. – Yishai

2

TDD no es una metodología, es una forma de pensar.

TDD para reducir errores: A medida que su base de códigos comienza a crecer, es muy importante que ejecute todas las pruebas en cada registro en el control de origen. El beneficio de esto se vuelve evidente cuando tienes un nuevo miembro en el equipo.

TDD como paradigma de diseño: Las pruebas son los primeros usuarios del código. Inicialmente, es muy difícil conducir su diseño usando pruebas. Pero, una vez que te sientas cómodo con él, te darás cuenta de que el código de prueba realmente te ayuda a decidir sobre tu diseño. Esto es algo natural con alguna experiencia TDD. Por ejemplo, con TDD, le gustaría ajustar el acceso a su servicio en una interfaz. Esto te permite burlarte. Pero, lo más importante aquí es que es la forma correcta de diseñar.

TDD como documentación La documentación es para la documentación de código destinada a ser utilizada por los desarrolladores de su código. A los desarrolladores les resulta fácil leer pruebas unitarias bien redactadas, en lugar de páginas y páginas de documentación.

1

En Javascript, o en cualquier idioma que tenga que ejecutarse en múltiples entornos peculiares como navegadores, las pruebas unitarias son una excelente forma de decir donde Internet Effing Explorer cometió un error qué arreglar.

Al menos es mejor que martillar F5 y usar console.log() o incluso alertas.

Pero, de nuevo, creo que esto se debe utilizar principalmente para middleware o RIA complejo, ya que es casi imposible para las interfaces de usuario TDD con poca lógica comercial.

¡Si escribe el próximo jQuery, las pruebas unitarias serán sus amigos! :)

Ah, y no nos olvidemos de las excelentes herramientas para js como JSTestDriver!

+0

Cierto, y ese es un caso bastante extremo para cuando las pruebas unitarias serían útiles: cuando la plataforma misma cambia y necesita asegurarse de que su código se comporte de manera consistente en todas ellas. En ese caso, realmente no tienes otra opción que probar todo con dolorosa meticulosidad. En general entiendo los incentivos para las pruebas unitarias más que para TDD. TDD todavía es realmente dudoso para mí. –

Cuestiones relacionadas