Ú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
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.
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.
@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.
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).
- 1. ¿Cómo puede el código SwingWorker hacerse comprobable
- 2. Código comprobable - ejemplo de aplicación pequeña
- 3. ¿Cómo decorar una clase como no comprobable para Cobertura de código?
- 4. ¿Cómo crear código comprobable usando clases de IO de .Net?
- 5. No se puede cambiar el estado de QListViewItem comprobable con widget personalizado
- 6. ¿Alguien ha probado el lino?
- 7. ¿Has probado utilizar MVC para el código de cliente GWT?
- 8. Android artículo comprobable Menú
- 9. ¿Cómo se preserva el código que NO funcionó?
- 10. PHPUnit :: ¿Cómo puede funcionar ese conjunto y obtener cookies, probado?
- 11. ¿Cómo no se puede conservar el código EF4 primero?
- 12. ¿Es ágil científicamente probado?
- 13. Buscar el código no utilizado
- 14. Creación de servicio WCF comprobable sin OperationContext
- 15. ¿Cómo funciona el código "IE6 no más"?
- 16. Código de copia de archivo simple probado y verdadero en C?
- 17. ¿Cómo usar el código administrado del código no administrado?
- 18. personalizada comprobable Ver que responde al selector
- 19. ¿Cómo se puede alternar entre los comentarios y el código no distinguible en IDEA?
- 20. ¿Por qué urllib.urlopen.read() no se corresponde con el código fuente?
- 21. Android - Junit - El proyecto probado usa Jar externo
- 22. No se puede detectar por qué el siguiente fragmento de código no se ha vectorizado
- 23. ¿Cómo se mantiene el código de desarrollo y el código de producción?
- 24. ¿Por qué el cortocircuito y el operador no se cortocircuitan?
- 25. ¿Por qué este código no se define pero no 2?
- 26. UISearchDisplayController no funciona cuando se crea en el código?
- 27. ¿Por qué este código no se bloquea?
- 28. ¿Existen proyectos documentados en los que se haya probado y rechazado CouchDB?
- 29. opciones del compilador para mezclar el ARC y el Código no se hace caso omiso ARC
- 30. ¿Cómo es que este código no se bloquea?