Recientemente completé una refactorización de caja negra. No puedo registrarlo porque no puedo averiguar cómo probarlo.
En un nivel alto, tengo una clase cuya inicialización implica tomar valores de alguna clase B. Si la clase B está "vacía", genera algunos valores predeterminados razonables. Extraje esta parte a un método que inicializa la clase B a los mismos valores predeterminados.
Todavía tengo que resolver el propósito / contexto de cualquiera de las clases, o cómo se utilizarían. Por lo tanto, no puedo inicializar el objeto desde una clase B vacía y verificar que tenga los valores correctos / haga lo correcto.
Mi mejor idea es ejecutar el código original, codificar los resultados de los métodos públicos en función de los miembros inicializados, y probar el nuevo código contra eso. No puedo articular por qué me siento vagamente incómodo con esta idea.
¿Hay un mejor ataque aquí?
fuente
Respuestas:
¡Lo estas haciendo bien!
Crear pruebas de regresión automatizadas suele ser lo mejor que puede hacer para que un componente sea refactible. Puede ser sorprendente, pero tales pruebas a menudo se pueden escribir sin la plena comprensión de lo que hace internamente el componente, siempre y cuando comprenda las "interfaces" de entrada y salida (en el significado general de esa palabra). Lo hicimos varias veces en el pasado para aplicaciones heredadas completas, no solo clases, y a menudo nos ayudó a evitar romper cosas que no entendíamos completamente.
Sin embargo, debe tener suficientes datos de prueba y asegurarse de tener una comprensión firme de lo que hace el software desde el punto de vista de un usuario de ese componente, de lo contrario corre el riesgo de omitir casos de prueba importantes.
En mi humilde opinión, es una buena idea implementar sus pruebas automatizadas antes de comenzar a refactorizar, no después, para que pueda refactorizar en pequeños pasos y verificar cada paso. La refactorización en sí debería hacer que el código sea más legible, por lo que le ayuda a aumentar su comprensión de las partes internas poco a poco. Entonces, los pasos de orden en este proceso son
fuente
Una razón importante para escribir pruebas unitarias es que documentan la API del componente de alguna manera. No entender el propósito del código que se está probando es realmente un problema aquí. La cobertura del código es otro objetivo importante, difícil de lograr sin saber qué ramas de ejecución existen y cómo se activan.
Sin embargo, si es posible restablecer el estado limpiamente (o construir el nuevo objeto de prueba cada vez), se pueden escribir pruebas de tipo "basura dentro-basura" que solo alimentan al sistema principalmente al azar y observan la salida.
Tales pruebas son difíciles de mantener, ya que cuando fallan, puede ser complejo decir por qué y qué tan grave es. La cobertura puede ser cuestionable. Sin embargo, todavía son mucho mejores que nada. Cuando dicha prueba falla, el desarrollador puede revisar los últimos cambios con más atención y, con suerte, detectar el error allí.
fuente