Actualmente estoy refactorizando una parte de una gran base de código sin pruebas unitarias de ningún tipo. Traté de refactorizar el código de forma bruta, es decir, tratando de adivinar qué está haciendo el código y qué cambios no cambiarían su significado, pero sin éxito: rompe aleatoriamente las características de todo el código base.
Tenga en cuenta que la refactorización incluye mover el código C # heredado a un estilo más funcional (el código heredado no usa ninguna de las características de .NET Framework 3 y posteriores, incluido LINQ), agregando genéricos donde el código puede beneficiarse de ellos, etc.
No puedo usar métodos formales , dado cuánto costarían.
Por otro lado, supongo que al menos la regla "Cualquier código heredado refactorizado vendrá con pruebas unitarias" debe seguirse estrictamente, sin importar cuánto costaría. El problema es que cuando refactorizo una pequeña parte de un método privado de 500 LOC, agregar pruebas unitarias parece ser una tarea difícil.
¿Qué puede ayudarme a saber qué pruebas unitarias son relevantes para un determinado código? Supongo que el análisis estático del código de alguna manera sería útil, pero ¿cuáles son las herramientas y técnicas que puedo usar para:
Sepa exactamente qué pruebas unitarias debo crear,
¿Y / o sabe si el cambio que hice afectó el código original de una manera que se ejecuta de manera diferente a la actual?
fuente
formal methods in software development
todos modos , no querría usarlo porque se usa para probar la exactitud de un programa usando lógica de predicados y no tendría aplicabilidad para refactorizar una base de código grande. Los métodos formales que generalmente se usan para probar el código funcionan correctamente en áreas como aplicaciones médicas. Tiene razón, es costoso hacerlo, por eso no se usa con frecuencia.Respuestas:
He tenido desafíos similares. El libro Working with Legacy Code es un gran recurso, pero se asume que puedes calzar zapatos en pruebas unitarias para apoyar tu trabajo. A veces eso simplemente no es posible.
En mi trabajo de arqueología (mi término para mantenimiento en código heredado como este), sigo un enfoque similar en cuanto a lo que usted describió.
En este punto, debe tener una lista de candidatos de lo que ha sido expuesto y / o manipulado por esa rutina. Es probable que algunas de esas manipulaciones sean involuntarias. Ahora uso
findstr
y el IDE para comprender qué otras áreas pueden hacer referencia a los elementos en la lista de candidatos. Pasaré un tiempo entendiendo cómo funcionan esas referencias y cuál es su naturaleza.Finalmente, una vez que me engañe pensando que entiendo los impactos de la rutina original, haré mis cambios uno por uno y volveré a ejecutar los pasos de análisis que describí anteriormente para verificar que el cambio esté funcionando como esperaba a trabajar Trato específicamente de evitar cambiar varias cosas a la vez, ya que descubrí que esto me afecta cuando trato de verificar el impacto. A veces puede salirse con la suya con múltiples cambios, pero si puedo seguir una ruta de uno en uno, esa es mi preferencia.
En resumen, mi enfoque es similar a lo que usted presentó. Es mucho trabajo de preparación; luego haga cambios circunspectos e individuales; y luego verificar, verificar, verificar.
fuente
Respuesta corta: pequeños pasos.
Considere estos pasos:
Mueva la implementación a una función diferente (privada) y delegue la llamada.
Agregue código de registro (asegúrese de que el registro no falle) en su función original, para todas las entradas y salidas.
Ejecute su aplicación y haga lo que pueda con ella (uso válido, uso no válido, uso típico, uso atípico, etc.).
Ahora tiene
max(call_count)
conjuntos de entradas y salidas para escribir sus pruebas; Podría escribir una sola prueba que repita todos sus parámetros / conjuntos de resultados que tenga y los ejecute en un bucle. También puede escribir una prueba adicional que ejecute una combinación particular (que se utilizará para verificar rápidamente el paso sobre un conjunto de E / S en particular).Mover
// 500 LOC here
de nuevo en suugly500loc
función (y eliminar la funcionalidad de registro).Comience a extraer funciones de la función grande (no haga nada más, solo extraiga funciones) y ejecute pruebas. Después de esto, debería tener más pequeñas funciones para refactorizar, en lugar de la 500LOC.
Vivir feliz para siempre.
fuente
Por lo general, las pruebas unitarias son el camino a seguir.
Realice las pruebas necesarias que prueben que la corriente funciona como se esperaba Tómese su tiempo y la última prueba debe hacerle confiar en la salida.
Estás en el proceso de refactorizar un fragmento de código, debes saber exactamente qué hace y qué impacto tiene. Básicamente, necesitas probar todas las zonas afectadas. Esto le llevará mucho tiempo ... pero ese es un resultado esperado de cualquier proceso de refactorización.
Entonces puedes desgarrar todo sin problemas.
AFAIK, no hay una técnica a prueba de balas para esto ... ¡solo debes ser metódico (en cualquier método con el que te sientas cómodo), mucho tiempo y mucha paciencia! :)
Saludos y buena suerte!
Alex
fuente