2010-01-26 11 views
14

Estoy trabajando con el código java heredado, sin ninguna prueba unitaria. Muchas clases necesitan ser refactorizadas para poder trabajar con el proyecto.Cómo validar que una refactorización es igual al código original

Muchas refactorizaciones se pueden hacer con eclipse, y estoy haciendo algunas a mano. Después de algunas refactorizaciones, reviso el diff to cvs-HEAD, pero realmente no puedo estar seguro de que todo sea 100% correcto.

La pregunta: ¿Cómo puedo validar una refactorización, que es matemática idéntica a la versión anterior? Ojalá hubiera una herramienta, pero también acepto "algoritmos humanos básicos" como soluciones.

Sé que "ejecutar tus JUnit-Tests" es la mejor respuesta, pero lamentablemente, no hay ninguno en mi proyecto.

¡Gracias!

+5

Escriba primero las pruebas JUnit? Al posponerlas para escribirlas (o no escribirlas en absoluto), puede pasar más tiempo solucionando errores que escribiendo las pruebas. –

+0

me parece que la refacturación automatizada usando un IDE como eclipse o intellij casi siempre le dará un código que es exactamente la misma semántica que el original, con muy pocos casos raros donde sale mal (sobre todo en llamadas Class.forname() y, a veces, Intellij se confunde y produce código que no compila). – Chii

Respuesta

34

En "TDD By Example" hay una sección específica que habla al respecto. El problema es que necesita pruebas unitarias para refactorizar, pero un código complicado generalmente no es comprobable. Por lo tanto, desea refactorizar para que sea comprobable. Ciclo.

lo tanto, la mejor estrategia es la siguiente:

Hacer pequeños pasos de refactorización. Cuando los pasos son pequeños, es más fácil para un humano asegurarse de que el comportamiento general esté intacto. Elija solo refactorizaciones que aumenten la capacidad de prueba. Este es tu objetivo inmediato. No piense en respaldar la funcionalidad futura (o cualquier otra cosa así). Solo piense en "cómo puedo hacer posible que una prueba unitaria pruebe este método/clase".

Tan pronto como un método/clase sea comprobable, escríbalo en pruebas unitarias.

La repetición de este proceso te llevará gradualmente a una posición en la que tienes pruebas y así podrás refactorizar más agresivamente. Por lo general, este proceso es más corto de lo que cabría esperar.

+1

Consejo genial. +1. –

+0

+1 Un gran consejo. Muchos y pequeños compromisos. Hazlo en estado comprobable y luego escribe pruebas unitarias y continúa. – Timothy

+0

+1 aka Refactor Fruta colgante baja: http://c2.com/cgi/wiki?RefactorLowHangingFruit –

4

Me temo que no existe un algoritmo que pueda validar que un programa sea semánticamente idéntico a otro programa; es un problema que se detiene y que se ha demostrado que no tiene solución.

Otro método ligeramente más automatizado sería comparar las salidas de ambos programas. Sin embargo, eso podría ser difícil para cualquier programa grande, ya que probablemente no haya un rango de entrada bien definido ...

tal vez es tiempo de que usted haya escrito las pruebas de unidad que considera que faltan.

editar: dado que aceptaría algoritmos humanos, esto es lo que suelo hacer. Estudiaría el código para ser refactorizado, y entendería su semántica. Escriba al menos una prueba unitaria, o algún tipo de prueba automatizada para esa parte de la base de códigos. Realice el refactorizador, luego vea si la prueba todavía pasa. Si lo hace, tiene buenas posibilidades de que el refactor (*) no rompa nada.

(*) aquí, me refiero a refactor para cambiar la implementación/algoritmo, etc., no solo cambiar el nombre y barajar el código y hacer líneas comunes de código en los métodos/clases base, etc. Lo que casi puede ver, siempre que tener una buena comprensión de la base de código.

2

Aún diría "ejecuta tus pruebas unitarias";). La única opción que tiene que estar realmente seguro de que el resultado es el mismo, es una prueba unitaria. Puede agregarlos usted mismo antes de refactorizar el código específico. Toma más tiempo al principio, pero le ahorrará mucho tiempo a largo plazo.

+0

¿Qué pasa si las pruebas de su unidad no son buenas? Por ejemplo, ¿no cubren algunos casos extremos? ¿O algún otro programador ha aparecido mientras tanto y ha agregado una función sin una prueba unitaria? ¿Cómo se asegura de que no rompa ese tipo de funcionalidad tampoco? – lexicalscope

8

Estás bajando por una ladera resbaladiza si crees que puedes detectarlo con solo mirar el programa. Y como ya dijo uno de los otros respondedores, la cuestión de si dos programas son iguales es indecidible (mediante una máquina de turing).

Si no tiene pruebas unitarias, le sugiero que al menos configure un arnés de prueba de regresión. Tome una instantánea de alguna entrada y alguna versión de salida 1 del programa toma/produce, ejecútelo a través de la versión dos y asegúrese de que los resultados sean los mismos.

Si se trata de una GUI, espero que tenga una separación MVC para que pueda probar el modelo por separado, de lo contrario, podría quedarse atascado.

5

La Pregunta: ¿Cómo puedo validar una refactorización , es matemática idéntica a la versión anterior? I desearía que hubiera una herramienta, pero también acepto "algoritmos humanos básicos" como soluciones.

estrictamente hablando, refactorizaciones son un conjunto de transformación bien conocido que se han demostrado a la preservación de la semántica del código. Ver Refactoring Object-Oriented Frameworks. Todo lo demás se debe llamar reingeniería, pero estoy de acuerdo en que ambos se usan indistintamente.

Razonar sobre la preservación de la semántica es difícil, y es un tema de investigación abierto. Ver por ejemplo Formalising Behaviour Preserving Program Transformations.

Hasta que tengamos una herramienta eficiente para verificar la preservación semántica, lo mejor es contar con probando. Otro enfoque para obtener más confianza en su cambio sería agregar aserciones y contratos. Te obligará a hacer una revisión y pensar sobre lo que podría haber cambiado, cuáles son las invariantes, qué podría romperse más a fondo.

3

La pregunta: ¿Cómo puedo validar una refactorización, que es matemática idéntica a la versión anterior? Desearía que hubiera una herramienta, pero también acepto los "algoritmos humanos básicos" como soluciones.

Estoy de acuerdo con Chii en que esto es intrínsecamente imposible. Si realmente necesita refactorizarlo (en lugar de escribir un adaptador para el código heredado, haciendo que sus cosas nuevas se relacionen libremente con el código anterior), debe tener especial cuidado con las subclases, los métodos anulados, etc. Las pruebas de unidad de escritura pueden ayudar, pero si no sabe realmente qué debería hacer el código, ¿cómo puede escribir pruebas unitarias para ello? Solo puede escribir pruebas unitarias para asegurarse de que el nuevo código haga lo que asumió que hizo el código anterior.

1

he hecho lo siguiente a un proyecto con unos enormes clases Dios con una necesidad de refactorización:

uso de AOP a 'volcar' su estado de objeto y los parámetros en el inicio de su método y al final. Vuelque el valor de retorno también.

Luego grabe una gran cantidad de escenarios con el código que necesita cambiar y repítalos con su código refactorizado.

Esto no es matemático, es un poco pesado de configurar, pero después de obtener un buen conjunto de pruebas de no regresión.

El dumper es fácil de configurar con una herramienta como XStream.

1

Me gustaría ir con la respuesta de Itay, pero simplemente dando otra opción.
Si está dispuesto a pagar, existe un producto de Agitar que genera automáticamente pruebas de JUnit para el código existente. Esas pruebas son pruebas de "caracterización", que están destinadas a probar que el código hace lo que hace actualmente. Luego, una vez que hagas los cambios, puedes ver que lo que se rompe es lo que querías cambiar solamente.

Más información está disponible here.

1

En teoría, podría definir un conjunto de transformaciones seguras (refactorizaciones) y crear un programa que verifique que el programa B es el resultado de aplicar un subconjunto finito de esas refactorizaciones al programa A. (Evite el problema de detención estableciendo un límite superior). Pero me temo que hacer un programa así es mucho más difícil que escribir las pruebas unitarias para el programa A, que es lo que realmente deberías hacer.

1

Estoy un poco sorprendido de que hasta ahora nadie haya mencionado el libro Working Effectively with Legacy Code, de Michael Feathers. Se trata de esta situación exacta, con muchos consejos prácticos, en varios idiomas. Lo recomiendo a cualquiera que se ocupe de proyectos heredados.

Cuestiones relacionadas