2010-07-14 8 views
6

escenario típico para mí:¿Cómo puedo generar el código fuente para crear un objeto que estoy depurando?

  1. El código heredado trabajo en tiene un error que sólo un cliente en la producción está teniendo
  2. que adjuntar un depurador y encontrar la manera de reproducir el problema en su sistema dado su entrada. Pero, no sé por qué el error está pasando todavía.
  3. Ahora quiero escribir una prueba automatizada en mi sistema local para tratar de reproducir a continuación, corregir el error

Ese último paso es muy duro. La entrada puede ser muy compleja y tener muchos datos. Crear la entrada a mano (p. Ej .: imagínese hacer esto 1000 veces para crear el objeto) es muy tedioso y propenso a errores. De hecho, puede notar que hay un error tipográfico en el ejemplo que acabo de dar.

¿Hay una manera automática de tomar un campo de un punto de interrupción en mi depurador y generar código fuente que crearía ese objeto, poblado de la misma manera?

Lo único que se me ocurre es serializar esta entrada (usando Xstream, por ejemplo). Puedo guardarlo en un archivo y volver a leerlo en una prueba automatizada. Esto tiene un problema importante: si la clase cambia de ciertas maneras (p. Ej .: se cambia el nombre de un campo/getter/setter), ya no podré deserializar el objeto. En otras palabras, las pruebas son extremadamente frágiles.

+1

¿Podría aclarar por qué algo como XStream no encaja y por qué es absolutamente necesario generar código fuente? Esto sin duda ayudaría a obtener mejores respuestas. –

+1

¿Cuál es el problema con la fragilidad a largo plazo de los casos de prueba para un evento de prueba de campo que desea resolver AHORA? –

+0

@IraBaxter No hay nada de malo en eso a corto plazo. Pero si hay alguna genio biblioteca/técnica/estrategia para lidiar con esto mejor de lo que estoy actualmente, me gustaría saberlo. –

Respuesta

2

La serialización estándar de Java es bien conocida por no ser muy útil cuando los objetos cambian su versión (contenido, nombres de campos). Está bien para proyectos demo rápidos.

más adecuado para sus necesidades, es el enfoque que objetcs apoyar su propia serialización (binario) personalizado:
Esto no es difícil, utilice DataOutputStream para escribir todos los campos de un objeto. Pero ahora introduce versiong, primero escribiendo un versionId. Los objetos que tienen solo una versión, escriben la versión Id 1. De esta forma, más tarde, cuando tenga que introducir un cambio en sus objetos, elimine campos, agregue campos, suba el número de versión.

Tal ICustomSerializable entonces leer primero el número de versión del flujo de entrada, en un método readObject(), y en función del identificador de versión llamar readVersionV1() o, por ejemplo readVersionV2().

public Interface ICustomSerializable { 
    void writeObject(DataOutputStream dos); 
    Object readObject(DataInputStream dis); 
} 

public Class Foo { 
    public static final VERSION_V1 = 1; 
    public static final VERSION_V2 = 2; 

    public static final CURRENT_VERSION = VERSION_V2; 
    private int version; 

    private int fooNumber; 
    private double fooDouble; 

    public void writeObject(DataOutputStream dos) { 
    dos.writeInt(this.version); 
     if (version == VERSION_V1) { 
      writeVersionV1(dos); 
     } else (version == VERSION_V2) { 
      writeVersionV2(dos); 
     } else { 
     throw new IllegalFormatException("unkown version: " + this.version); 
     } 
    } 
    public void writeVersionV1(DataOutputStream dos) { 
     writeInt(this.fooNumber); 
     writeDouble(this.fooValue); 
    } 
} 

Además getter y setter, y se necesita un constructor con la versión inicializada en CURRENT_VERSION.

Este tipo de serialización es seguro para la refactorización si cambia o agrega también la versión de lectura y escritura apropiada. Para objetos complejos que utilizan clases de librerías externas y no de su control, puede ser más trabajo, pero las cadenas de caracteres se pueden serializar fácilmente.

+0

Ese es un enfoque interesante. De hecho, estaba pensando en escribir mi propio serializador xstream para escribir el código fuente (si es que incluso es una opción). Lo que no me gusta de esta sugerencia es que romper la compilación probablemente sea lo que me notifique. Tengo que implementar 'writeVersionV2'. No me gustaría escribirlo bajo esa condición. –

1

Creo que lo que quiere hacer es almacenar el "estado" y luego restaurarlo en la prueba para asegurarse de que el error permanezca fijo.

Respuesta corta: No existe una herramienta de generación de código general así, pero mientras se conserven varias restricciones, escribir una herramienta de este tipo es un trabajo pequeño.

Comentario largo: Existen restricciones bajo las cuales eso puede funcionar. Si todo es solo beans con getter y setter para todos los campos que necesita, entonces generar código para esto no es tan difícil. Y sí, sería seguro cambiarle el nombre si refactoriza el código generado junto con el código normal. Si falta el setter, entonces este enfoque no funcionará. Y ese es solo un ejemplo de por qué esta no es una solución general.

La refabricación también puede, por ejemplo, mover campos a otras clases. ¿Cómo desea introducir los valores de los otros campos de esa clase? ¿Cómo puede saber más adelante si los que alteraron su estado guardado aún reflejan los datos críticos? O peor, imagine que la refactorización le da al mismo campo un significado diferente al anterior.

La naturaleza del error en sí también es una limitación. Imagínese, por ejemplo, que el error ocurrió porque un campo/método tenía este y ese nombre. Si una refactorización ahora cambia el nombre, el error ya no aparecerá, independientemente de su estado.

Esos son simplemente ejemplos arbitrarios, que pueden no tener nada que ver exactamente con sus casos de la vida real. Pero esta es una decisión de caso por caso, no una estrategia general. De todos modos, si conoce su código, el error y sus refactorizaciones se están comportando lo suficientemente bien para esto, entonces hacer esa herramienta se realiza en menos de un día, probablemente mucho menos.

Con xstream obtendrías esto parcialmente también, pero tendrías que cambiar el xml tú mismo. Si utilizó, por ejemplo, db4o, debería decirle que este y ese campo tienen ahora este y ese nombre.

Cuestiones relacionadas