¿Debo escribir pruebas cuando pueda probar la corrección del código?

8

La gente dice que "hablar sobre TDD apenas funciona, si quieres convencer a alguien de TDD, muéstrale los resultados". Sin embargo, ya estoy obteniendo excelentes resultados sin TDD. Mostrándome que las personas que usan TDD obtienen buenos resultados no serán convincentes, quiero ver que las personas que escriben tanto TDD como no TDD obtengan mejores resultados con TDD.

A pesar de todo esto, estoy interesado en probar TDD. Sin embargo, no estoy convencido de que gane nada de esto. Si resulta útil, intentaré llevarlo al resto de mi equipo.

Mi pregunta principal es esta: ¿serviría TDD para algún propósito del código, si ya puedo probar la corrección del código?

Obviamente, ninguno de los dos es una bala de plata. Su prueba puede estar equivocada porque omitió un detalle, y su prueba podría fallar al detectar un error que no pudo probar. Al final, somos humanos, nadie puede hacer código 100% libre de errores para siempre. Solo podemos esforzarnos por acercarnos lo más posible.

Sin embargo, ¿TDD realmente ahorraría tiempo en código que tuviera su corrección probada? es decir, código que, en la máquina de estado en la que opera el código, todos los estados posibles válidos y sus rangos son reconocidos por el desarrollador, todos se tienen en cuenta y el código está diseñado en una verificación de errores de estilo de lista blanca que pasa todas las excepciones a un controlador superior para asegurarse de que no haya fugas inesperadas -> sin mostrar un mensaje relevante (dentro de lo razonable) al cliente y sin enviar notificaciones de registro a un administrador.

Las respuestas con ejemplos de la vida real serían mejores.


Algunas aclaraciones:

  • Esta pregunta no se trata de si puede probar la corrección del código o no. Asumamos por defecto que no todo el código puede probarse correcto dentro de un plazo razonable, pero que algunos fragmentos de código pueden serlo. Por ejemplo, es muy fácil comprobar la corrección de un módulo FizzBuzz. No es muy fácil para un servicio de sincronización de datos basado en la nube.

  • Dentro de este límite, la pregunta plantea lo siguiente: Comience con la suposición de que una base de código se divide en 2 partes: [I] partes que se han demostrado correctas [II] partes que no se han demostrado correctas, pero que se probaron manualmente para que funcionen.

  • Quiero aplicar prácticas TDD a esta base de código que no las tenía hasta ahora. La pregunta es la siguiente: ¿se debe aplicar TDD a cada módulo o sería suficiente aplicarlos solo a los módulos que no se probaron correctamente?

  • "Probado correcto" significa que puede considerar este módulo con un estilo completamente funcional, es decir, no se basa en ningún estado global o externo fuera de sí mismo, y tiene completamente su propia API para E / S que deben seguir otros módulos que interactúan con él. . No es posible "romper este módulo" cambiando el código fuera del módulo, en el peor de los casos, puede usarlo mal y recibir mensajes de error formateados.

  • Obviamente, cada regla tiene excepciones, los errores del compilador en las nuevas versiones del compilador pueden introducir errores en este módulo, pero podrían introducirse los mismos errores en las pruebas que lo probaron y dar como resultado una falsa sensación de seguridad de las pruebas que ya no funcionan según lo previsto. La conclusión es que las pruebas no son una solución mágica, son otra capa de protección, y esta pregunta discute el tema de si esta capa de protección vale la pena en el caso específico de un módulo que se demostró que es correcto (suponga que era de hecho).

Kylee
fuente
10
Probar la "corrección del código" podría ser más difícil de lo que crees.
πάντα ῥεῖ
34
Si bien disfruté aprendiendo sobre su lugar de trabajo y su historial laboral, casi dejé de leer varias veces porque, bueno, a veces lo que es realmente importante es eso, como una forma de maximizar sus resultados y, para mantener la atención de su lector para que puedan, en un esfuerzo por fortalecer su conocimiento y el de la comunidad, ayudarlo, debe ... ir al grano . Considere acortar su pregunta a sus puntos más destacados.
MetaFight
10
Lo que usted describe no es un proceso de "prueba de corrección" del código. Ese tipo de proceso es drásticamente diferente. Además, me resulta difícil aceptar que pueda construir código de una manera que pueda "probarse correcta" con solo mirarlo. Vi un montón de código que parecía trivial y "correcto", solo para ser totalmente aplastado cuando se sometió a una prueba automatizada robusta.
Eufórico el
8
"Tenga cuidado con los errores en el código anterior; solo he demostrado que es correcto, no lo he probado". -Donald Knuth.
Neil
66
Tu pregunta no tiene sentido. TDD significa que las pruebas impulsan el desarrollo. En otras palabras, no tiene diseño, arquitectura ni código, a menos que tenga una prueba para ello. Entonces, ¿cómo en el mundo está "aplicando TDD al código que se ha demostrado que es correcto" cuando, según la definición misma de TDD, no hay un código que demuestre que es correcto ?
Jörg W Mittag

Respuestas:

20

Si.

Las pruebas están bien cuando están disponibles, pero incluso en el mejor de los casos, solo prueban que un solo bit de código funcionará como se esperaba (para todas las entradas, que representan interrupciones en el medio de cualquier operación), ¿qué hay de quedarse sin memoria? ? falla de disco? falla de red?).

¿Qué pasa cuando cambia?

Las pruebas son excelentes porque sirven como un contrato implícito sobre lo que debe hacer el código. Proporcionan algunos andamios para que su nuevo pasante pueda entrar y hacer cambios con cierto nivel de confianza. Todo a través de resultados rápidos y claros: pasar o fallar.

Y, francamente, puedo entrenar a un pasante para escribir pruebas unitarias viables en unos pocos meses. Dudo que alguien en mi equipo (incluido yo mismo) pueda crear pruebas que garanticen algo significativo para un código no trivial; y mucho menos hacerlo de forma rápida y precisa.

Telastyn
fuente
El mismo código que no sería trivial de probar tampoco sería trivial de probar. Una falla en la red o una falla en el disco puede tomar muchas formas, ¿cómo puede estar seguro de que sus pruebas cubrieron todos los escenarios posibles? Lo más probable es que cubran todos los escenarios imaginables, pero no necesariamente todos los escenarios de la vida real. No puede simplemente asumir ciegamente que cada cambio no se romperá solo porque tiene pruebas establecidas. Es solo otra capa de protección. Las pruebas son importantes, pero no son una bala de plata.
Kylee
66
@Kylee: no te ofendas, pero pareces estar subestimando enormemente el esfuerzo y la habilidad necesarios para hacer una prueba de corrección (o sobreestimando enormemente el esfuerzo y la habilidad necesarios para realizar algunas pruebas automatizadas).
Telastyn el
Tal vez tengas razón, y lo más probable es que sobreestime el esfuerzo de armar pruebas debido a que no tengo experiencia (honestamente, lo que me preocupa es menos las "pruebas de armado" y más acerca de ejecutar las pruebas). Para proyectos grandes y que cambian rápidamente, eso en sí mismo no es un sumidero de tiempo insignificante). De cualquier manera, esto no tiene nada que ver con la pregunta. La pregunta dice claramente: suponga que tiene un código que ya se probó correcto, ¿todavía hay un punto para escribir pruebas unitarias para él?
Kylee
1
For large & rapidly changing projectscuanto más propensos a cambiar, más necesarias son las pruebas, ya que un código cambiante tiene muchas más posibilidades de fallar debido a nuevos errores o comportamientos inesperados, que el código que apenas cambia. Es una cuestión de probabilidad. Incluso si no cambia a menudo, después de un tiempo el conocimiento obtenido durante el desarrollo podría perderse o caer en el olvido. Las pruebas también son conocimientos materializados, que pueden reducir, significativamente, la curva de aprendizaje. ¿Las pruebas de codificación requieren mucho tiempo? Si. ¿Hace más caro el proyecto? No, a la larga lo hace más barato .
Laiv
3
@Kylee Incluso si pudieras demostrar que un código es correcto, en el mejor de los casos estarías en la situación de que a nadie se le permite cambiar ese código o agregarle funciones; hacerlo invalidaría su prueba. Como ya se destacó en esta respuesta, las pruebas le permiten cambiar su código con confianza. Incluso si la persona que lo cambia es un interno sin experiencia. Por cierto, incluso si algún bloque de código es 100% correcto, no significa que nunca tendrá que cambiarlo (por ejemplo, para agregar una nueva característica que necesita un cliente o para satisfacer alguna restricción del mundo real que no se considera en su prueba).
Brandin
5

No lo sabemos No podemos responder tu pregunta.

Si bien pasa mucho tiempo explicando que el proceso que tiene ahora parece funcionar para satisfacción de todos, nos está contando solo una pequeña parte de lo que realmente está sucediendo.

Según mi experiencia, lo que está describiendo es una rareza extrema y soy escéptico de que sea realmente su proceso y enfoque de codificación lo que en realidad sea la causa del bajo recuento de errores en sus aplicaciones. Puede haber muchos otros factores que influyen en sus aplicaciones y no nos dice nada sobre esos factores.

Por lo tanto, no sabemos, frente a no conocer su entorno y cultura de desarrollo exactos, si TDD lo ayudará o no. Y podemos pasar días discutiendo y discutiendo al respecto.

Solo podemos darte una recomendación: pruébalo. Experimentar. Aprenderlo. Sé que estás tratando de gastar la menor cantidad de esfuerzo para decidir, pero eso no es posible. Si realmente desea saber si TDD funcionará en su contexto, la única forma de averiguarlo es hacer TDD. Si realmente lo aprende y lo aplica a su solicitud, puede compararlo con su proceso que no es TDD. Puede ser que TDD realmente tenga ventajas y usted decida conservarlo. O puede resultar que TDD no trae nada nuevo y solo te ralentiza. En cuyo caso, puede recurrir a su proceso anterior.

Eufórico
fuente
5

El propósito principal de las pruebas (unitarias) es salvaguardar el código, asegurándose de que no pase desapercibido debido a cambios posteriores. Cuando se escribe el código por primera vez, recibirá mucha atención y se examinará. Y es posible que tenga un sistema superior para eso.

Seis meses después, cuando alguien más está trabajando en algo que aparentemente no está relacionado, puede romperse y su superduper probador de corrección de código no lo notará. Una prueba automática lo hará.

Martin Maat
fuente
1
Esto es algo que con demasiada frecuencia se pasa por alto. Sí, es difícil pasar para asegurarse de que el código tenga pruebas unitarias ahora. Pero esto se amortizará muchas veces cuando un desarrollador junior realice un cambio simple y las pruebas unitarias denuncien de inmediato que han roto algo más. He sido el desarrollador junior hace décadas que ha roto algo y todo el lanzamiento se ha retrasado. Si bien mis colegas eran muy tolerantes en ese momento y me respaldaban cuando los gerentes comenzaron a saltar hacia arriba y hacia abajo, todo el escenario podría, en retrospectiva, haberse evitado si se hubieran realizado pruebas unitarias.
Robbie Dee
5

Quiero aplicar prácticas TDD a esta base de código que no las tenía hasta ahora.

Esta es la forma más difícil de aprender TDD. Cuanto más tarde realice la prueba, más le costará escribir las pruebas y menos obtendrá de escribirlas.

No digo que sea imposible adaptar las pruebas a una base de código existente. Estoy diciendo que es probable que hacerlo no convierta a nadie en un creyente TDD. Este es un trabajo duro.

En realidad, es mejor practicar TDD la primera vez en algo nuevo y en casa. De esa manera aprendes el ritmo real. Haga esto bien y lo encontrará adictivo.

La pregunta es la siguiente: ¿debe aplicarse TDD a cada módulo?

Eso es pensamiento estructural. No debe decir cosas como probar cada función, clase o módulo. Esos límites no son importantes para las pruebas y deberían poder cambiar de todos modos. TDD se trata de establecer una necesidad de comportamiento comprobable y no preocuparse de cómo se satisface. Si no fuera así, no podríamos refactorizar.

¿O sería suficiente aplicarlos solo a los módulos que no fueron probados correctamente?

Es suficiente aplicarlos donde los necesite. Empezaría con un nuevo código. Obtendrá mucho más de vuelta de la prueba temprana que tarde. No hagas esto en el trabajo hasta que hayas practicado lo suficiente como para dominarlo en casa.

Cuando haya demostrado que TDD es eficaz con el nuevo código en el trabajo y se siente lo suficientemente seguro como para asumir el código anterior, comenzaría con el código probado. La razón es porque podrás ver de inmediato si las pruebas que estás escribiendo están llevando el código en una buena dirección.

Mi pregunta principal es esta: ¿serviría TDD para algún propósito del código, si ya puedo probar la corrección del código?

Las pruebas no solo prueban la corrección. Muestran intención. Muestran lo que se necesita. Señalan un camino hacia el cambio. Una buena prueba dice que hay varias formas de escribir este código y obtener lo que desea. Ayudan a los nuevos programadores a ver lo que pueden hacer sin romper todo.

Solo una vez que tenga eso abajo, debe deambular por el código no probado.

Una advertencia contra los fanáticos: parece que has logrado el éxito y, por lo tanto, es poco probable que saltes de cabeza. Pero otros que buscan probarse a sí mismos no serán tan reservados. TDD puede ser exagerado. Es increíblemente fácil crear un conjunto de pruebas que realmente perjudica la refactorización porque bloquean cosas triviales y sin sentido. ¿Como sucedió esto? Porque las personas que buscan mostrar pruebas simplemente escriben pruebas y nunca refactorizan. ¿Solución? Hazlos refactorizar. Haz que se ocupen de los cambios de funciones. Cuanto antes mejor. Eso te mostrará las pruebas inútiles rápidamente. Usted demuestra flexibilidad flexionando.

Una advertencia contra la categorización estructural: algunas personas insistirán en que una clase es una unidad. Algunos llamarán a cualquier prueba con dos clases una prueba de integración. Algunos insistirán en que no puedes cruzar el límite x y lo llamarán una prueba unitaria. En lugar de preocuparse por nada de eso, le aconsejo que se preocupe por cómo se comporta su prueba. ¿Se puede ejecutar en una fracción de segundo? ¿Se puede ejecutar en paralelo con otras pruebas (sin efectos secundarios)? ¿Se puede ejecutar sin iniciar o editar otras cosas para satisfacer dependencias y condiciones previas? Puse estas consideraciones por delante si habla con una base de datos, sistema de archivos o red. ¿Por qué? Porque estos tres últimos son solo problemas porque causan los otros problemas. Agrupe sus pruebas en función de cómo puede esperar que se comporten. No los límites que pasan para cruzar. Entonces sabrá lo que puede esperar que haga cada conjunto de pruebas.

He visto a personas decir que no quieren usar TDD porque tendría demasiada sobrecarga, y los partidarios de TDD lo defienden diciendo que una vez que te acostumbras a escribir TDD todo el tiempo no hay mucha sobrecarga.

Esa pregunta ya tiene respuestas aquí .

naranja confitada
fuente
1

Test Driven Development tiene más que ver con la creación de prototipos y la lluvia de ideas de una API que con las pruebas. Las pruebas creadas son a menudo de baja calidad y eventualmente tienen que descartarse. La principal ventaja de TDD es determinar cómo se utilizará una API, antes de escribir la implementación de la API. Esta ventaja también se puede obtener de otras maneras, por ejemplo, escribiendo documentación de API antes de la implementación.

Las pruebas de corrección son siempre más valiosas que las pruebas. Las pruebas no prueban nada. Sin embargo, para utilizar las pruebas de corrección de manera productiva, es útil contar con un verificador de pruebas automatizado, y deberá trabajar utilizando contratos de algún tipo (diseño por contrato o diseño basado en contrato).

En el pasado, cuando trabajaba en secciones críticas de código, intentaba pruebas de corrección manual. Incluso las pruebas informales son más valiosas que cualquier prueba automatizada. Pero aún necesita las pruebas, a menos que pueda automatizar sus pruebas, ya que las personas romperán su código en el futuro.

Las pruebas automatizadas no implican TDD.

Frank Hileman
fuente
La lluvia de ideas sobre una API y el diseño por contrato ya es la forma en que abordo los problemas, por lo que me gustaría decir que me gusta su respuesta, pero otras fuentes dicen "En el desarrollo basado en pruebas, cada nueva característica comienza con escribir una prueba". Dijiste cosas con las que estoy de acuerdo, pero no me convenciste para usar el enfoque TDD. "Las pruebas automatizadas no implican TDD", está bien, pero entonces, ¿qué implica TDD, en lugar de simplemente seguir las mejores prácticas generales?
Kylee
1
Si bien estoy de acuerdo en que TDD a menudo implica una lluvia de ideas sobre una API, IMO, esto a veces es algo malo . La API no debe estar diseñada (necesariamente) para la capacidad de prueba, debe estar diseñada para que sus clientes la utilicen sin problemas y para que tenga sentido para las clases / código involucrados. Con demasiada frecuencia he visto a un escritor de pruebas decir "agreguemos String getSomeValue()aquí para que podamos probarlo" cuando eso no tiene sentido para el diseño general. Claro, podría eliminar esa función más tarde, pero, en mi experiencia, eso es raro.
user949300
1
@ user949300, existe una gran superposición entre el diseño de una API comprobable y una diseñada para que sus clientes la utilicen sin problemas. Agregar código innecesario por el bien de la prueba muestra que tiene un mal diseño. Con demasiada frecuencia, los escritores de API se olvidan de depurar lo que está mal con una API. Escribir algo comprobable Y útil para el usuario te obliga a pensar en esas cosas ... sin filtrar detalles de implementación en tu interfaz.
Berin Loritsch
@ user949300 La queja número uno sobre TDD es probablemente la forma en que la API se modifica para "verificabilidad", en el sentido de la unidad, de modo que las cosas que generalmente están encapsuladas están expuestas. La queja número dos es probablemente que no escala bien con el tiempo.
Frank Hileman el
@Kylee Las pruebas automatizadas son más antiguas que la palabra de moda "TDD". La diferencia es que las pruebas automatizadas son simplemente lo que crees que deberían probarse: no hay dogma ni un orden específico en el que las escribas. Tampoco hay ningún énfasis en las pruebas de unidad versus integración.
Frank Hileman
0

A) Al leer el código y convencerse de que es correcto, no está ni remotamente cerca de demostrar que es correcto. De lo contrario, ¿por qué escribir pruebas?

B) Cuando cambie el código, desea ejecutar pruebas que demuestren que el código sigue siendo correcto o no.

Josh
fuente
esto parece simplemente repetir los puntos hechos (y mucho mejor explicados) en la respuesta superior que se publicó unas semanas antes
mosquito
@gnat Es una respuesta más sucinta para algo que no necesita una novela escrita al respecto. Intenta contribuir con algo aquí.
Josh
-1

Voy a advertir que una vez que esté acostumbrado a usar TDD de manera efectiva, le ahorrará tiempo al final del juego. Se necesita práctica para aprender a usar TDD de manera efectiva, y no ayuda cuando estás en una crisis de tiempo. Cuando aprenda a utilizarlo mejor, le recomiendo comenzar un proyecto personal en el que tenga más margen de maniobra y menos presión en el horario.

Verás que tu progreso inicial es más lento mientras experimentas más y escribes tu API. Con el tiempo, su progreso será más rápido a medida que sus nuevas pruebas comiencen a pasar sin cambiar el código, y tenga una base muy estable para construir. En el juego tardío, el código que no se construye con TDD requiere que pases mucho más tiempo en el depurador mientras tratas de descubrir qué está yendo mal de lo que debería ser necesario. También corre mayor riesgo de romper algo que solía funcionar con nuevos cambios. Evaluar la efectividad de TDD versus no usarlo por el tiempo total hasta su finalización.

Dicho esto, TDD no es el único juego en la ciudad. Puede usar BDD, que usa una forma estándar de expresar el comportamiento de una aplicación de pila completa, y evaluar la corrección de la API desde allí.

Todo su argumento depende de "probar la corrección del código", por lo que necesita algo que defina la corrección del código. Si no está utilizando una herramienta automatizada para definir qué significa "correcto", entonces la definición es muy subjetiva. Si su definición de correcto se basa en el consenso de sus pares, eso puede cambiar en un día determinado. Su definición de correcto debe ser concreta y verificable, lo que también significa que debe poder ser evaluada por una herramienta. ¿Por qué no usar uno?

La victoria número 1 al usar pruebas automáticas de cualquier tipo es que puede verificar que su código siga siendo correcto incluso cuando los parches del sistema operativo se aplican de manera rápida y eficiente. Ejecute su suite para asegurarse de que todo esté pasando, luego aplique el parche y vuelva a ejecutar la suite. Aún mejor, hágalo parte de su infraestructura de construcción automatizada. Ahora puede verificar que su código siga siendo correcto después de fusionar el código de varios desarrolladores.

Mi experiencia con TDD me ha llevado a las siguientes conclusiones:

  • Es excelente para el nuevo código, difícil para cambiar los sistemas heredados
  • Todavía tiene que saber lo que está tratando de lograr (es decir, tener un plan)
  • Lento para comenzar, pero ahorra tiempo más tarde
  • Lo obliga a pensar en cómo validar la corrección y la depuración desde la perspectiva del usuario

Mi experiencia con BDD me ha llevado a las siguientes conclusiones:

  • Funciona tanto para código heredado como nuevo.
  • Valida toda la pila y define la especificación.
  • Más lento para ponerse en marcha (ayuda a tener a alguien que conozca el conjunto de herramientas)
  • Es necesario definir menos comportamientos que las pruebas unitarias

Definición de correcto: su código cumple con los requisitos. Esto se verifica mejor con BDD, que proporciona un medio para expresar esos requisitos de una manera legible para los humanos y verificarlos en tiempo de ejecución.

No estoy hablando de la corrección en términos de pruebas matemáticas, lo cual no es posible. Y estoy cansado de tener esa discusión.

Berin Loritsch
fuente
Una publicación maravillosa que aborda un problema que me hizo considerar TDD para empezar: la definición actual de "corrección de código" es de hecho un consenso de los pares. Mi preocupación es que a medida que el equipo cambie en el futuro, este método dejaría de funcionar. Pero, ¿cómo resolvería esto TDD? Si bien las pruebas anteriores pueden evitar que los nuevos miembros del equipo rompan fácilmente la funcionalidad anterior, aún pueden escribir pruebas incompletas para funciones futuras, lo que aún generaría problemas. Al final, ambos métodos se basan en confiar en su equipo. No he oído hablar de BDD antes, así que voy a ver eso también. Gracias.
Kylee
2
"puede verificar que su código siga siendo correcto" No. Ni siquiera cerca. Como máximo, puede afirmar que la prueba que ha ejecutado aún pasa.
Doblado el
@Kylee, BDD aborda mejor esa preocupación. BDD tiene que escribir una especificación que sea verificable. La especificación está escrita en su lenguaje natural con medios para crear enlaces al código de prueba real que verifica la especificación. Eso une dos preocupaciones, comunicando los requisitos reales y haciéndolos cumplir.
Berin Loritsch
1
@Bent, por favor no discuta sobre la "corrección" en términos de pruebas matemáticas. Ese no es el tema de conversación o lo que pretendía transmitir. Sin embargo, según la experiencia, las personas que publican comentarios como el suyo tienden a tener eso en mente. Puede verificar absolutamente que el código cumple con los requisitos. Esa es la definición de trabajo correcta de la que estoy hablando.
Berin Loritsch
El problema es que está conduciendo a una definición de "correcto", donde el requisito es que el código pase las pruebas que ha definido. Cualquier otro conjunto de entradas es un comportamiento indefinido, y la salida es arbitraria. Esto realmente no coincide con lo que la mayoría de los usuarios consideraría que son los requisitos.
Simon B