¿Tiene sentido agregar pruebas unitarias para un código heredado conocido?

21
  • Estoy hablando de pruebas unitarias en el sentido TDD. (No es una "integración" automatizada, o lo que le gusta llamar pruebas).
  • Código heredado como en: código (C ++) sin pruebas. (ver: Michael Feathers ' Trabajando efectivamente con código heredado )
  • Pero también el código heredado como en: Código con el que nuestro equipo ha estado trabajando durante los últimos 10-5 años, por lo que a menudo tenemos una muy buena idea de dónde poner las cosas para cambiar algo.
  • Tenemos pruebas unitarias (a través de Boost.Test) para algunos módulos que vinieron más tarde o que han sido "naturales" para pruebas unitarias (contenedores específicos de aplicaciones comunes, elementos de cadena, ayudantes de red, etc.)
  • Todavía no tenemos pruebas de aceptación automatizadas adecuadas.

Ahora, recientemente tuve el "placer" de implementar 3 nuevas funciones orientadas al usuario.

Cada uno de ellos me tomó aproximadamente 1-2 horas para ponerme al día con las partes del código que necesitaba cambiar, 1-2 horas para implementar el (pequeño) código que necesitaba cambiar y otras 1-2 horas para asegurarme de que la aplicación funcionó correctamente después e hizo lo que se suponía que debía hacer.

Ahora, realmente agregué un pequeño código. (Creo que hay un método y algunas líneas de llamada para cada función).

Factorizar el código (a través de cualquiera de los métodos sugeridos en WEwLC ), de modo que una unidad de prueba hubiera algún sentido (y no ha sido una tautología completa) habría fácilmente tomado otras 2-4 horas, si no más. Esto habría agregado un 50% -100% de tiempo a cada función, sin beneficio inmediato, ya que

  1. No necesitaba la prueba unitaria para entender nada sobre el código
  2. La prueba manual es la misma cantidad de trabajo, ya que aún necesito probar si el código está integrado correctamente en el resto de la aplicación.

De acuerdo, si , más adelante, "alguien" vino y tocó ese código, teóricamente podría obtener algún beneficio de esa prueba de unidad. (Solo teóricamente, ya que esa isla de código probada viviría en un océano de código no probado).

Entonces, "esta vez" decidí no hacer el trabajo duro de agregar una prueba unitaria: los cambios en el código para obtener esas cosas bajo prueba habrían sido significativamente más complejos que los cambios en el código para implementar la función correctamente (y limpiamente).

¿Es esto algo típico para código heredado fuertemente acoplado? ¿Soy flojo / establecemos las prioridades equivocadas como equipo? ¿O soy prudente, solo probando cosas donde la sobrecarga no es demasiado alta?

Martin Ba
fuente
¿Qué pasa si otro departamento se hará cargo de su "código heredado conocido"? ¿Qué harán los chicos con eso?
Alex Theodoridis

Respuestas:

18

Tienes que ser pragmático sobre estas situaciones. Todo tiene que tener un valor comercial, pero la empresa tiene que confiar en usted para juzgar cuál es el valor del trabajo técnico. Sí, siempre hay un beneficio en tener pruebas unitarias, pero ¿es ese beneficio lo suficientemente bueno como para justificar el tiempo dedicado?

Yo diría siempre en el nuevo código, sino, por el código heredado, lo que tiene que hacer un juicio.

¿Estás en esta área de código a menudo? Luego hay un caso para la mejora continua. ¿Estás haciendo un cambio significativo? Luego hay un caso de que ya es un código nuevo. Pero si está creando un código de una línea en un área compleja que probablemente no se volverá a tocar durante un año, por supuesto, el costo (sin mencionar el riesgo) de la reingeniería es demasiado grande. Simplemente ingrese su línea de código y báñese rápidamente.

Regla general: siempre piense: " ¿Creo que el negocio se beneficia más de este trabajo técnico que creo que debo hacer que el trabajo que me pidieron que se retrasará como resultado? "

pdr
fuente
¿Qué es un buen código? ¿Una que esté bien diseñada, probada, documentada y a prueba de futuro o que le paguen? Como siempre: puedes tenerlo bien ; puedes tenerlo barato ; Puedes tenerlo rápido . Elija dos, el tercero es su costo.
Sardathrion - Restablece a Monica el
2
"Yo argumentaría siempre sobre el nuevo código" ... ¿"nuevo código" en una base de código heredada o "nuevo código nuevo"? :-)
Martin Ba
pdr tiene la idea correcta. Es una decisión de ingeniería (una con compensaciones). En general, si tiene una gran pieza de código examinado, es una buena idea dejarlo solo. He visto un esfuerzo de TDD en el código heredado (solo un año, pero eso es suficiente) terminó costando meses y solo terminó gastando dinero y rompiendo una aplicación heredada durante meses hasta que se corrigieron los errores introducidos por el refactorizador TDD. Si el código / característica está hecho y probado, no lo rompas para agregar instrumentación (TDD) para decirte algo que ya sabías de todos modos (que funciona).
anon
10

La ganancia de las pruebas unitarias en el sentido TDD es obtener algo que se mantenga por sí mismo, sin necesidad de una tradición oral construida durante años por un equipo estable.

Es una gran suerte que el mismo equipo haya estado en el mismo proyecto durante tanto tiempo. Pero esto eventualmente cambiará. Las personas se enferman, aburren o ascienden. Se mueven, se retiran o mueren. Los nuevos vienen con nuevas ideas y la necesidad de refactorizar todo de la forma en que aprendieron en la escuela. Las empresas se venden y compran. Las políticas de gestión cambian.

Si desea que su proyecto sobreviva, debe prepararlo para los cambios.

Mouviciel
fuente
3

Escribir pruebas unitarias es una prueba futura de su código. Si la base del código no tiene pruebas, entonces debe agregar nuevas pruebas para todas las características nuevas porque hace que ese fragmento de código sea un poco más robusto.

Encuentro que agregar pruebas, incluso en código heredado, es beneficioso a mediano y largo plazo. Si su empresa no se preocupa por el mediano a largo plazo, buscaría otra empresa. Además, no creo que lleve más tiempo probarlo manualmente que escribir una prueba unitaria establecida. Si lo hace, entonces necesita practicar código kata enfocado en escribir pruebas.

Sardathrion - Restablece a Monica
fuente
1
¡Pero pero pero! :-) Ese poco debería traducirse en un aumento del 100% en el tiempo necesario para implementar una nueva característica. No , no disminuir el tiempo necesario para implementar características siguientes. Se puede disminuir el tiempo necesario al cambiar cosas.
Martin Ba
3
Se está asegurando de que los cambios en esa área del código no introduzcan nuevos errores, que los nuevos (usuarios menos experimentados) puedan obtener una comprensión clara del código, y que los efectos secundarios que afectan esa área son tos en el momento de la prueba en lugar de en el lanzamiento hora. Las pruebas ayudan a verificar que el código hace lo que no debería hacer más fácil agregar características más adelante. Encuentro que agregar pruebas, incluso en código heredado, es beneficioso a mediano y largo plazo . Si su empresa no se preocupa por el mediano a largo plazo, buscaría otra empresa.
Sardathrion - Restablece a Monica el
1
@ Martin porque claramente lo que haces es codificar lo que crees que debería ser la característica, y luego enviarla. No se realiza ninguna prueba en ningún momento ... no, espera. Como desarrolladores, todos probamos nuestro código (al menos a mano) antes de decir que está hecho. Reemplazar "a mano" con "escribiendo una prueba automatizada" no es un aumento del 100% en el tiempo. Con frecuencia, encuentro que es casi el mismo tiempo que se tarda.
Kaz Dragon
1
@Kaz - "Reemplazar" a mano "con" al escribir una prueba automatizada "no es un aumento del tiempo del 100%. Con frecuencia, encuentro que es casi el mismo tiempo". - YMMV, pero para mi base de código, esto es claramente incorrecto (como se establece en la pregunta).
Martin Ba
3
@Kaz: Lo que quería decir :-) era: Una prueba unitaria prueba mi código de forma aislada. Si no escribo una prueba unitaria, no pruebo el código de forma aislada, sino solo si el código se llama correctamente y produce el resultado deseado en la aplicación. Estos dos puntos (llamados correctamente y app-does-what-it-should) debo probar de todos modos (manualmente) y no están cubiertos por una prueba unitaria. Y la prueba unitaria no cubriría estos dos puntos, sino que "simplemente" probaría mi función de forma aislada. (Y esta prueba de unidad de prueba sería adicional y no sería compensada por nada hasta donde puedo ver).
Martin Ba
2

Ciertamente, las mejores prácticas dictan que debe realizar una prueba unitaria de cualquier código que cambie. Sin embargo, la codificación del mundo real implica muchos compromisos, el tiempo y los materiales son limitados.

Lo que debe hacer es evaluar el costo en términos reales de corregir errores y realizar modificaciones sin pruebas unitarias. Entonces puede evaluar la inversión en la creación de esas pruebas. Las pruebas unitarias reducen en gran medida el costo del trabajo futuro, pero ahora tienen un precio.

La conclusión es que si su sistema rara vez cambia, entonces puede que no valga la pena escribir pruebas unitarias, pero si está sujeto a cambios regulares, entonces valdrá la pena.

Tom Squires
fuente
1

Todas estas pequeñas decisiones racionales para no crear pruebas están acumulando una deuda técnica paralizante que volverá a atormentarte cuando alguien se vaya y un nuevo empleado tiene que entrar y ponerse al día.

Sí, podría haber costado otras 2-3 horas escribir las pruebas unitarias, pero si el código se devuelve después de las pruebas, tendrá que probar manualmente todo nuevamente y documentar sus pruebas nuevamente, ¿lo hace, no? De lo contrario, ¿cómo sabe alguien lo que probó y qué valores utilizó?

Las pruebas unitarias documentan el comportamiento esperado del código que usaron los datos de prueba para verificar la funcionalidad y les da a los nuevos codificadores una confianza razonable de que no han roto algo al hacer cambios.

Cuesta un par de horas hoy más que las pruebas manuales, pero luego está probando cada vez que realiza algún cambio, ahorrando horas durante la vida útil del código.

Sin embargo, si sabe que el código será retirado en un par de años, todo lo que he dicho cambia porque no terminará de poner las pruebas en el código y nunca pagará.

mcottle
fuente
"y documentar sus pruebas, de nuevo, ¿lo haces, no?" - {poniéndose el sombrero cínico:} Por supuesto que no. No estamos en tierra de hadas desarrollador. Las cosas se implementan, prueban y envían. Más tarde se rompe, repara, prueba y envía.
Martin Ba
Espero que todo se pruebe manualmente, independientemente del número de pruebas unitarias. Las pruebas de aceptación automatizadas son un asunto completamente diferente. Pero las pruebas de aceptación no se hacen más difíciles debido a que la base de código es heredada, por lo que están fuera de alcance aquí.
pdr
2
mcottle: te falta un punto vital . La prueba de la unidad no disminuirá el tiempo necesario para realizar la "prueba de funcionalidad manual" de la función finalizada. Yo todavía tengo que hacer clic a través de todos los diálogos que participan con un subconjunto prudente de posibles entradas para asegurarse de que la aplicación acabada real, construido hace lo que tiene que hacer. (Admito que , teóricamente, la unidad de pruebas puede ayudar a limitar este ciclo de prueba manual a un solo bucle si las pruebas unitarias se aseguran de que no encontrará ningún problema evidente sólo durante las pruebas manuales.)
Martin Ba
0

El valor de las pruebas unitarias no solo radica en probar una nueva característica cuando se desarrolla. El beneficio de las pruebas unitarias se obtiene principalmente en el futuro.

Sí, es posible que tenga que pasar lo que parece una cantidad excesiva de tiempo para hacer que su código heredado sea comprobable.

Pero si no lo hace, pasará, o ciertamente debería , pasar muchas, muchas, muchas más horas probando la función. No solo en su desarrollo inicial, sino con cada nueva versión de su software. Sin las pruebas unitarias y otras formas de pruebas automatizadas, no tiene otra opción que pasar muchas horas de pruebas manuales para asegurarse de que su función no se haya roto accidentalmente, incluso cuando esa función en sí no se modificó.

Marjan Venema
fuente
1
"Sin las pruebas unitarias, no tiene ningún otro medio para asegurarse de que su función no se haya roto accidentalmente, incluso cuando esa función en sí misma no haya cambiado". - Las pruebas unitarias no ayudarán. Para asegurarme realmente , tendría que tener pruebas de integración / aceptación automatizadas.
Martin Ba
Sí, las pruebas unitarias son solo el primer paso. Respuesta editada para aclarar
Marjan Venema