¿Cómo escribo pruebas unitarias para código heredado (que no entiendo)?


8

Adelante

He leído muchas cosas antes de hacer esta pregunta, incluidas muchas preguntas relevantes aquí en SE:

Sin embargo, no puedo evitar sentir que aún no he rascado la picazón después de leer para pedir ayuda.


TL; DR

¿Cómo escribo pruebas unitarias para código heredado que no puedo ejecutar, simular, leer o entender fácilmente? ¿Qué pruebas de regresión son útiles para un componente que presumiblemente funciona según lo previsto?


La imágen completa

Vuelvo a ser pasante de verano nuevamente mientras estoy haciendo la transición a la escuela de posgrado. Mi tarea implica estos requisitos:

  1. Para un producto en particular, evalúe si nuestro equipo de software puede actualizar su versión IDE y JUnit sin perder la compatibilidad con sus proyectos existentes.
  2. Desarrolle pruebas unitarias para algún componente en el código Java existente (en gran medida no es Java). Queremos convencer al equipo de software de que las pruebas unitarias y TDD son herramientas invaluables que deberían utilizar. (Actualmente hay un 0% de cobertura de código).
  3. De alguna manera, termine los días de codificación de vaquero para un sistema crítico.

Después de obtener una copia del código fuente, intenté compilarlo y ejecutarlo, para poder entender qué hace este producto y cómo funciona. No pude Le pregunté a mis supervisores cómo me iba, y me dieron una nueva máquina independiente capaz de construirlo, incluidos los scripts de compilación que realmente funcionan. Eso tampoco funcionó porque, como deberían haber esperado, su código de producción solo se ejecuta en el sistema integrado para el que está diseñado. Sin embargo, tienen un simulador para este propósito, por lo que obtuvieron el simulador y me lo pusieron en esta máquina. El simulador tampoco funcionó. En cambio, finalmente recibí una copia impresa de una GUI para una pantalla en particular. Tampoco tienen comentarios de código en ningún lugar dentro del 700 LOC de Java, lo que hace que sea aún más difícil de entender. Además, hubo problemas al evaluar si sus proyectos eran compatibles o no con IDE más nuevos. Particularmente, su código no se cargó correctamente en la versión IDE que usan.

Mi inventario se ve así:

  • NetBeans 8, 9, 10, 11
  • JUEGO 4, 5
  • Su código fuente para un producto en particular (incluye más de 700,000 Java LOC)
  • Prácticamente no hay comentarios de código (ocasionalmente una firma)
  • No hay pruebas existentes
  • Una foto física de una ventana GUI
  • Un documento de diseño de software (109 p.) Que no discute el componente en la imagen

Al menos tengo suficiente para escribir teóricamente pruebas que puedan ejecutarse. Entonces, probé una prueba de unidad básica en este componente mencionado. Sin embargo, no pude inicializar los objetos que tenía como dependencias, que incluían modelos, gerentes y conexiones de base de datos. No tengo mucha experiencia en JUnit más allá de las pruebas unitarias básicas, así que sígame en la siguiente sección.


Lo que aprendí de mi lectura

  1. Burlarse: si escribo una prueba unitaria, es probable que necesite tener variables simuladas para dependencias de producción que no puedo inicializar fácilmente setUp.
  2. Todos aquí sugieren generosamente el libro "Trabajando efectivamente con código heredado" de Michael Feathers.
  3. Las pruebas de regresión son probablemente un buen lugar para comenzar. No creo que tenga suficiente armamento para intentar las pruebas de integración, y las pruebas de regresión proporcionarían más gratificación instantánea a nuestro equipo de software. Sin embargo, no tengo acceso a sus errores conocidos; pero, posiblemente podría preguntar.

Y ahora un intento de articular la incertidumbre que todavía tengo como una pregunta. Esencialmente, no entiendo cómo parte de escribir estas pruebas. Asumiendo que no recibo ninguna guía adicional de mis supervisores (probablemente), está en mi estadio no solo aprender qué hace este componente sino también decidir qué pruebas son realmente útiles como pruebas de regresión.

Como profesionales que han trabajado con proyectos como este por más tiempo que yo, ¿pueden ofrecer alguna guía sobre cómo escribir pruebas unitarias en este tipo de situación?


12
How do I write unit tests for legacy code that I can't build, run, simulate, read about, or otherwise understand?No puedes. Al menos debe saber cuál es la salida esperada para una entrada dada.
Robert Harvey

1
¿Ya leíste el libro de Michael Feathers?
Robert Harvey

@RobertHarvey Es una ligera hipérbole. Obviamente, puedo tomarme el tiempo de leer el código para entenderlo, pero ese es típicamente el último recurso para algo tan grande. Y no, aún no lo he hecho, ya que encontré el libro esta mañana.
Joshua Detwiler

Si tiene código heredado que ni siquiera puede construir, ¿está seguro de que tiene la versión correcta del código? Primero debe resolver esto antes de preocuparse por las pruebas automatizadas: ¿el código que está tratando de construir es igual al que ejecutan sus usuarios? El primer paso en todo esto si tuviera un sistema en funcionamiento sería tratar de entenderlo desde el punto de vista del usuario; puede ayudar a leer la documentación del usuario y tal vez mirar los datos del usuario si es posible para tratar de descubrir qué hace
Ben Cottrell

2
Si los datos del sensor son cruciales para la ejecución, burlarse de los sensores (o lo que sean las dependencias externas) parece ser un buen lugar para comenzar.
JimmyJames

Respuestas:


10

Para una primera aproximación, las partes interesadas de un conjunto de pruebas son los desarrolladores / mantenedores de código. Necesitarás algo de su tiempo. Insiste en esto.

Pregúnteles sobre los problemas que enfrentan.
Pregúnteles sobre los errores que han solucionado recientemente (en los últimos años, suponiendo que este sea un proyecto largo y lento).

Tengo la impresión de que no esperas que sean amables con tu trabajo; quizás tengas razón. Pero no creo que puedas construir nada útil sin su ayuda.

Te tomarán la mano al escribir la primera prueba. Tal vez los arrastres, pero te tomarás de las manos de cualquier manera.

Una vez que tenga una prueba, con suerte será más claro cómo escribir la segunda. Si ha logrado construir algún tipo de relación, sin duda será más claro.


¡Gracias por la respuesta! ¿Podrías también echar un vistazo a la segunda mitad de mi pregunta? Mi primera mitad fue una pequeña duplicación de las preguntas que vinculé, y estaba principalmente interesado en la otra parte de mi pregunta por eso.
Joshua Detwiler

No sé lo útil que puedo ser. ¿Tiene problemas para entender cómo funciona el marco de prueba? ¿Te preguntas qué probar? Para eso, las pruebas de regresión son una buena opción: ¿Qué prueba habría evitado que este error que acabamos de solucionar no sea empujado, si hubiéramos sido tan perspicaces ?
ShapeOfMatter

Era más "¿Qué pruebas de regresión son útiles para un componente que presumiblemente funciona según lo previsto?" Por ejemplo, si voy a crear pruebas que sean útiles, ¿qué tiene de útil ser totalmente ciego sobre lo que funciona, lo que no funciona y cómo funciona? Existo en un vacío aparte del equipo de software y las personas que me dieron esta tarea existen en un vacío aparte de los dos.
Joshua Detwiler

Todavía estoy seguro de que debe insistir en entrar en la misma habitación que las personas que escriben el software, idealmente durante un par de días, y probablemente más de una vez. Puedo relacionarme con situaciones de trabajo absurdas impuestas, pero en algún momento uno necesita arriesgar su trabajo o aceptar que solo es un calentador de banco, supongo
ShapeOfMatter

Con respecto a las pruebas de regresión: no es conceptualmente diferente de cualquier otra prueba: ¿qué quisiera que el programa haga (o no haga) antes de reclamarle a un compañero de trabajo que estaba funcionando? Si hay registros de nuevas características o errores recientes, esos son buenos lugares para comenzar. O simplemente revise la documentación que tiene y elija algo que parezca comprobable. Java ha escrito funciones, lo cual es bueno. Una firma de tipo clara puede decirle mucho sobre qué esperar de una función (y puede considerarse como una especie de prueba unitaria en sí misma). Verificar el comportamiento NULL / empty-string / max-int también puede ser bueno.
ShapeOfMatter

3

Asumiré que en algún momento puedes obtener el código para al menos compilar. Si ni siquiera puedes llegar tan lejos, estás haciendo un recado de tontos.


La falta de requisitos, especificaciones o capturas de pantalla adecuados no es un bloqueador para escribir pruebas. Mientras pueda leer el código fuente, puede escribir pruebas.

Si se le da permiso para refactorizar la base del código para aislar cosas como conexiones de base de datos detrás de su propia interfaz, es posible escribir algunas pruebas de unidad de recuadro negro, básicamente escribir pruebas para arrojar alguna entrada a un método y afirmar su comportamiento o salida. Obtenga pruebas que cubran cada línea de código en un método y luego haga que uno de los miembros principales del equipo revise sus pruebas.

Si no tiene permiso para refactorizar la base del código para escribir pruebas unitarias, entonces las pruebas de integración completa o las pruebas de automatización de la interfaz de usuario son su única opción. Incluso entonces, la prueba de caja negra es su mejor estrategia: arroje algo de información a la interfaz de usuario y vea cómo reacciona. Haz tus afirmaciones. Haga que un miembro senior del equipo revise sus pruebas.

En algún momento tendrá suficientes pruebas automatizadas para comenzar a refactorizar la base del código con confianza para introducir pruebas unitarias. Las pruebas de IU aseguran que los principales casos de uso de negocios funcionen, y luego puede adaptar una arquitectura que conduzca a pruebas de nivel de unidad o componente.

Otro beneficio de las pruebas de IU es que puede construir una reputación con su equipo de que entiende la base del código, lo que a su vez los hace más abiertos a la introducción de cambios, porque la prueba está en el budín. Y habrás hecho budín escribiendo pruebas de aprobación.

En breve:

  • Escriba pruebas de Black Box como pruebas unitarias o de UI automatizadas
  • Haga que los miembros mayores revisen sus exámenes

Se sorprendería de lo rápido que puede aprender la vista panorámica de una aplicación de 700,000 líneas


1

Según la descripción del problema y sus comentarios, creo que lo mejor que puede hacer es comenzar con la API de Java e intentar construir una única prueba unitaria en torno a un método aislado.

Sin acceso al código, solo puedo darle una guía limitada, pero buscaría algo que en él a) no tenga dependencias b) no realice cambios de estado. Por ejemplo. Digamos que hay un método que toma un valor y comprueba que cae en un rango dado. Si no puede encontrar algo sin dependencias, intente algo que recupere un valor de una dependencia e intente simularlo.

Una vez que encuentre algo pequeño como esto, puede comenzar a construir pruebas. Si el método prueba un valor positivo, páselo negativo y asegúrese de que lo atrapa, etc. El problema aquí es que es posible que no sepa con certeza cuál es el comportamiento correcto. Es posible que deba pedir ayuda o buscar documentación para ello.

Es poco probable que llegue muy lejos con esto. La realidad es que escribir código para que pueda ser probado por la unidad es un arte en sí mismo. El código que no fue escrito con esto en mente puede ser difícil de imposible para la prueba unitaria.

Otra opción que puede implementarse más fácilmente es la prueba de compatibilidad binaria. Es decir, captura las entradas y salidas de un sistema y luego, para probar, alimenta esas mismas entradas y compara las salidas. Para empezar, esto no le indicará si el código es correcto o incorrecto, pero puede ayudar a detectar errores de regresión en los que las cosas cambiaron involuntariamente al realizar alguna otra modificación. Sin embargo, deberá poder ejecutar toda la aplicación para que esto suceda.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.