2008-08-10 14 views
8

Últimamente tuve que cambiar un código en sistemas más antiguos donde no todo el código tiene pruebas unitarias.
Antes de hacer los cambios, quiero escribir pruebas, pero cada clase creó muchas dependencias y otros patrones que dificultaron las pruebas.
Obviamente, quería refactorizar el código para que sea más fácil probarlo, escribir las pruebas y luego cambiarlo.
¿Es así como lo harías? ¿O pasaría mucho tiempo escribiendo las pruebas difíciles de escribir que se eliminarían en su mayoría luego de que se complete la refactorización?¿Cómo se prueba/cambia el código no probado y no comprobable?

Respuesta

5

Primero de todos, here's a great article with tips on unit testing. En segundo lugar, encontré una gran manera de evitar hacer muchos cambios en el código antiguo, solo para refactorizarlo un poco hasta que pueda probarlo. Una manera fácil de hacerlo es proteger a los miembros privados y luego anular el campo protegido.

Por ejemplo, supongamos que tiene una clase que carga algunas cosas de la base de datos durante el constructor. En este caso, no puede anular un método protegido, pero puede extraer la lógica de base de datos a un campo protegido y luego anularla en la prueba.

public class MyClass { 
    public MyClass() { 
     // undesirable DB logic 
    } 
} 

convierte

public class MyClass { 
    public MyClass() { 
     loadFromDB(); 
    } 

    protected void loadFromDB() { 
     // undesirable DB logic 
    } 
} 

y después de la prueba se ve algo como esto:

public class MyClassTest { 
    public void testSomething() { 
     MyClass myClass = new MyClassWrapper(); 
     // test it 
    } 

    private static class MyClassWrapper extends MyClass { 
     @Override 
     protected void loadFromDB() { 
      // some mock logic 
     } 
    } 
} 

Esto es algo así como un mal ejemplo, porque se podía utilizar DBUnit en este caso, pero De hecho, hice esto en un caso similar recientemente porque quería probar algunas funcionalidades totalmente ajenas a los datos que se estaban cargando, por lo que fue muy efectivo. También he encontrado que exponer a los miembros es útil en otros casos similares en los que necesito deshacerme de alguna dependencia que ha estado en una clase durante mucho tiempo.

Recomendaría esta solución si está escribiendo un marco, a menos que realmente no le importe exponer a los miembros a los usuarios de su marco.

Es un poco complicado, pero lo he encontrado bastante útil.

0

No estoy seguro de por qué diría que las pruebas unitarias se eliminarán una vez que se haya completado la refactorización. En realidad, su conjunto de pruebas unitarias debe ejecutarse después de la compilación principal (puede crear una compilación de "pruebas" separada, que solo ejecute las pruebas de la unidad una vez que se haya construido el producto principal). Luego verá inmediatamente si los cambios en una pieza rompen las pruebas en otro subsistema. Tenga en cuenta que es un poco diferente que ejecutar pruebas durante la compilación (como algunos pueden recomendar): algunas pruebas limitadas son útiles durante la compilación, pero generalmente es improductivo "bloquear" la construcción solo porque algunas pruebas de la unidad fallan.

Si está escribiendo Java (lo más probable es que lo haga), consulte http://www.easymock.org/ - puede ser útil para reducir el acoplamiento para fines de prueba.

3

@valters

estoy de acuerdo con su declaración de que las pruebas no deben romper la acumulación. Las pruebas deben ser una indicación de que la aplicación no tiene nuevos errores introducidos para la funcionalidad que se prueba (y un error encontrado es una indicación de una prueba faltante).

Si las pruebas no rompen la compilación, entonces puede encontrarse fácilmente en una situación en la que el código nuevo rompe la construcción y no se sabe por un tiempo, aunque una prueba lo cubrió. Una prueba de falla debe ser una bandera roja que indique que la prueba o el código debe ser reparado.

Además, permitiendo que las pruebas para no romper la acumulación hará que la tasa de fracaso a la fluencia lentamente, hasta el punto en que ya no tienen un conjunto fiable de pruebas de regresión.

Si hay un problema con las pruebas que se rompen con demasiada frecuencia, puede ser una indicación de que las pruebas se escriben de manera demasiado frágil (dependencia de los recursos que podrían cambiar, como la base de datos sin utilizar DB Unit correctamente, o un servicio web externo que debe ser burlado), o puede ser una indicación de que hay desarrolladores en el equipo que no prestan la atención adecuada a las pruebas.

Creo firmemente que una prueba no debe fijarse lo antes posible, del mismo modo que corregir código que falla al compilar lo antes posible.

0

He leído Working Effectively With Legacy Code, y estoy de acuerdo en que es muy útil para tratar con código "no comprobable".

Algunas técnicas sólo se aplican a los lenguajes compilados (estoy trabajando en "viejos" aplicaciones PHP), pero yo diría que la mayor parte del libro es aplicable a cualquier idioma.

Los libros de refacturación a veces asumen que el código está en estado semi ideal o "de mantenimiento" antes de refactorizar, pero los sistemas en los que trabajo no son ideales y se desarrollaron como aplicaciones "learn as you go" o como primeras aplicaciones para algunas tecnologías utilizadas (y no culpo a los desarrolladores iniciales por eso, ya que soy uno de ellos), por lo que no hay pruebas en absoluto, y el código a veces es complicado. Este libro aborda este tipo de situación, mientras que otros libros de refactorización generalmente no (bueno, no en este sentido).

Debo mencionar que no he recibido dinero del editor ni autor de este libro;), pero me pareció muy interesante, ya que faltan recursos en el campo del código heredado (y particularmente en mi idioma, Francés, pero esa es otra historia).

Cuestiones relacionadas