2009-02-06 9 views
11

Acabo de leer los primeros cuatro capítulos de Refactoring: Improving the Design of Existing Code, me embarqué en mi primera refactorización y casi inmediatamente llegué a un obstáculo. Se deriva del requisito de que antes de comenzar la refactorización, debe realizar pruebas unitarias alrededor del código heredado. Eso le permite estar seguro de que su refactorización no cambió lo que hizo el código original (solo cómo lo hizo).Refactorización práctica usando pruebas unitarias

Así que mi primera pregunta es esta: ¿cómo puedo probar la unidad de un método en el código heredado? ¿Cómo puedo poner una prueba unitaria alrededor de un método de 500 líneas (si tengo suerte) que no hace solo una tarea? Me parece que tendré que refactorizar mi código heredado solo para que pueda ser comprobado por la unidad.

¿Alguien tiene alguna experiencia en refactorizar usando pruebas unitarias? Y, si es así, ¿tiene algún ejemplo práctico que pueda compartir conmigo?

Mi segunda pregunta es algo difícil de explicar. Aquí hay un ejemplo: quiero refactorizar un método heredado que rellena un objeto de un registro de base de datos. ¿No tendría que escribir una prueba unitaria que compare un objeto recuperado utilizando el método anterior, con un objeto recuperado utilizando mi método refactorizado? De lo contrario, ¿cómo sabría que mi método refactorizado produce los mismos resultados que el método anterior? Si eso es cierto, ¿cuánto tiempo dejo el viejo método obsoleto en el código fuente? ¿Simplemente lo golpeo después de probar algunos registros diferentes? O bien, ¿debo guardarlo por un tiempo en caso de encontrar un error en mi código refactorizado?

Por último, ya que un par de personas han preguntado ... el código heredado se escribió originalmente en VB6 y luego se transfirió a VB.NET con cambios mínimos de arquitectura.

+0

Una gran pregunta. También puede probar Katas, que le ayuda a hacerse un hábito para escribir un buen código y cómo puede escribir código probado en unidades: https://github.com/garora/TDD-Katas –

Respuesta

4

Buen ejemplo de la teoría de la realidad. Las pruebas unitarias están destinadas a probar una sola operación y muchos puristas de patrones insisten en Single Responsibilty, por lo que tenemos código y pruebas encantadores. Sin embargo, en el mundo real (desordenado), el código (especialmente el código heredado) hace muchas cosas y no tiene pruebas. Lo que esto necesita es una dosis de refactorización para limpiar el desastre.

Mi enfoque es construir pruebas, usando las herramientas Unit Test, que prueban muchas cosas en una sola prueba. En una prueba, puedo estar comprobando que la conexión de la base de datos está abierta, cambiando muchos datos y haciendo una comprobación de antes/después en la base de datos. Inevitablemente me encuentro escribiendo clases de ayuda para hacer la comprobación, y la mayoría de las veces esos ayudantes pueden agregarse a la base de código, ya que han encapsulado comportamiento/lógica/requisitos emergentes. No me refiero a que tengo una sola prueba enorme, lo que quiero decir es que las pruebas de Mnay están haciendo un trabajo que un purista llamaría una prueba de integración. ¿Todavía existe algo así? También me pareció útil crear una plantilla de prueba y luego crear muchas pruebas a partir de eso, para verificar las condiciones de contorno, el procesamiento complejo, etc.

¿De qué entorno de lenguaje estamos hablando? Algunos lenguajes se prestan a la refactorización mejor que otros.

+0

A menudo me encuentro abogando por un compromiso en el lado pragmático de una discusión purista como esta. Tal vez estoy envejeciendo y no tengo la lucha dentro de mí para liderar la carga de los puristas. ;) – JMD

+0

@ JMD. Aún más, la única prueba unitaria significativa para la refactorización es la prueba unitaria funcional de extremo a extremo con mínima dependencia de la estructura interna. Al mismo tiempo, nadie habla de eso. Mi suposición es que hay una refactorización muy pequeña: el código existente es tan malo que en la mayoría de los casos la refactorización se hace reemplazando las unidades funcionales. – zzz777

0

Ese es realmente uno de los problemas clave de tratar de restablecer el código heredado. ¿Eres capaz de dividir el dominio del problema en algo más granular? ¿Ese método de línea de más de 500 hace algo más que llamadas al sistema a archivos JAR/DLL/ensamblados JDK/Win32/.NET Framework? Es decir. ¿Hay llamadas de función más granulares dentro de ese límite de línea de más de 500+ que podría probar una unidad?

+0

De hecho, hay más funciones granulares pero cómo ¿Puedo probarlos sin primero refactorizar el código heredado para extraer los métodos más granulares? –

+0

Esa es la pregunta de $ 64,000. Y a veces la respuesta es un compromiso. Desea esforzarse por una refactorización perfecta, pero a veces tiene que escribir qué pruebas unitarias puede/mientras/está mejorando el código heredado. Al menos esa ha sido mi experiencia. – JMD

1

Según mi experiencia, escribiría pruebas no para métodos particulares en el código heredado, sino para la funcionalidad general que proporciona. Estos pueden o no pueden acercarse a los métodos existentes.

1

Escriba pruebas en cualquier nivel del sistema que pueda (si puede), si eso significa ejecutar una base de datos, etc. entonces que así sea.Tendrá que escribir mucho más código para afirmar lo que el código está haciendo actualmente, ya que un método de 500 líneas + posiblemente tendrá mucho comportamiento envuelto en él. En cuanto a comparar lo viejo con lo nuevo, si escribes las pruebas contra el código anterior, pasan y cubren todo lo que hace, entonces cuando las ejecutas contra el nuevo código estás efectivamente comprobando lo viejo frente a lo nuevo. Hice esto para probar un disparador de sql complejo que quería refactorizar, fue un dolor y llevó tiempo, pero un mes después, cuando encontramos otro problema en esa área, valió la pena tener las pruebas allí para confiar.

9

Para obtener instrucciones sobre cómo refactorizar el código heredado, es posible que desee leer el libro Working Effectively with Legacy Code. También hay una versión corta en PDF disponible here.

+0

Genial para tener el enlace al PDF: ¡buen trabajo! – MarkJ

+0

+1 Este libro responde precisamente la pregunta. –

1

Según mi experiencia, esta es la realidad cuando trabajo en el código Legacy. El libro (Working with Legacy ..) mencionado por Esko es un excelente trabajo que describe varios enfoques que pueden llevarte allí.

He visto problemas similares con mi propia unidad de prueba, que se ha convertido en una prueba de sistema/funcional. Lo más importante para desarrollar pruebas para código heredado o existente es definir el término "unidad". Puede ser incluso una unidad funcional como "leer desde la base de datos", etc. Identificar unidades funcionales clave y realizar pruebas que agreguen valor.

Como nota aparte, hubo una conversación reciente entre Joel S. y Martin F. sobre TDD/pruebas unitarias. Mi opinión es que es importante definir la unidad y mantener el foco en ella. URLs: Open Letter, Joel's transcript y podcast

0

El libro siguiente: The Art of Unit Testing contiene un par de capítulos con algunas ideas interesantes sobre cómo tratar con código anterior en cuanto al desarrollo de pruebas unitarias.

Me pareció bastante útil.