2010-07-14 9 views
6

scenario tipico per me:Come posso generare il codice sorgente per creare un oggetto che sto eseguendo il debug?

  1. Il codice legacy Io lavoro su ha un bug che solo un cliente nella produzione sta avendo
  2. ho collegare un debugger e capire come riprodurre il problema su loro sistema dato l'input. Ma, non so perché l'errore si sta ancora verificando.
  3. Ora voglio scrivere un test automatico sul mio sistema locale per cercare di riprodurre poi correggere il bug

Questo ultimo passo è davvero difficile. L'input può essere molto complesso e avere molti dati. Creare l'input a mano (es: immagina di farlo 1000 volte per creare l'oggetto) è molto noioso e soggetto a errori. In effetti potresti notare un errore di battitura nell'esempio che ho appena dato.

C'è un modo automatico per prelevare un campo da un punto di interruzione nel mio debugger e generare un codice sorgente che creerebbe quell'oggetto, popolato nello stesso modo?

L'unica cosa che è venuta in mente è quello di serializzare questo ingresso (usando Xstream, per esempio). Posso salvarlo in un file e leggerlo di nuovo in un test automatico. Questo ha un grosso problema: se la classe cambia in determinati modi (ad esempio: un nome campo/getter/setter viene rinominato), non potrò più deserializzare l'oggetto. In altre parole, i test sono estremamente fragili.

+1

Potrebbe chiarire perché qualcosa come XStream non si adatta e perché è assolutamente necessario generare codice sorgente? Questo sicuramente aiuterebbe a ottenere risposte migliori. –

+1

Qual è il problema con la fragilità a lungo termine dei casi di test per un evento di test sul campo che vuoi risolvere ORA? –

+0

@IraBaxter Non c'è niente di sbagliato in questo a breve termine. Ma se c'è una bella biblioteca/tecnica/strategia là fuori per affrontare meglio di quanto non sia attualmente, mi piacerebbe sentirne parlare. –

risposta

2

La serializzazione standard Java è ben noto per non essere molto utile quando gli oggetti modificano la loro versione (contenuto, denominazione dei campi). Va bene per progetti demo veloci.

più adatto alle vostre esigenze, è l'approccio che objetcs sostenere la propria serializzazione (binario) personalizzato:
Questo non è difficile, utilizzare DataOutputStream di scrivere tutti i campi di un oggetto. Ma ora introducete versiong, scrivendo prima un versionId. Gli oggetti che hanno solo una versione, scrivono versionId 1. In questo modo puoi in seguito, quando devi introdurre una modifica nei tuoi oggetti, rimuovere campi, aggiungere campi, aumentare il numero di versione.

Tale ICustomSerializable sarà poi la prima leggere il numero di versione dal flusso di input, in un metodo readObject() e, a seconda della versione Id chiamare readVersionV1() o es 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); 
    } 
} 

Ulteriori getter e setter, e un costruttore con inizializzato la versione in CURRENT_VERSION è necessario.

Questo tipo di serializzazione è sicuro per il refactoring se si modifica o si aggiunge anche la versione di lettura e scrittura appropriata. Per oggetti complessi che utilizzano classi da librerie esterne e non dal tuo controller, può essere più utile, ma stringhe e liste sono facilmente serializzabili.

+0

Questo è un approccio interessante. In effetti, stavo pensando di scrivere il mio serializzatore xstream per scrivere il codice sorgente (se anche questa è un'opzione). La cosa che non mi piace di questo suggerimento è che la rottura del build sarà probabilmente la cosa che mi avvisa che devo implementare 'writeVersionV2'. Non vorrei scriverlo sotto questa condizione. –

1

Penso che ciò che si vuole fare è memorizzare il "stato", e quindi ripristinare che il test per garantire il bug rimane fissa.

Risposta breve: c'è per quanto ne so non esiste strumento di generazione di codice generale, ma fintanto che diversi vincoli sono tenuti, a scrivere un tale strumento è piccola opera.

lungo Commento: Ci sono vincoli cui è in grado di lavorare. Se tutto è solo bean con getter e setter per tutti i campi necessari, quindi generare codice per questo non è così difficile. E sì, sarebbe sicuro rinominare se si rifatta il codice generato insieme al codice normale. Se il setter manca, allora questo approccio non funzionerà. E questo è solo un esempio del perché questa non è una soluzione generale.

refactoring può anche, ad esempio spostare i campi ad altre classi. Come vuoi introdurre i valori degli altri campi di quella classe? Come puoi sapere in seguito se quelli che hanno modificato lo stato salvato riflettono ancora i dati critici? O peggio, immagina che il refactoring dia allo stesso campo un significato diverso rispetto a prima.

La natura del bug stesso è anche un vincolo. Immagina per esempio che il bug sia successo perché un campo/metodo aveva questo e quel nome. Se un refactoring ora cambia il nome, il bug non apparirà più a prescindere dal tuo stato.

Questi sono solo esempi arbitrarie, che possono avere esattamente nulla a che fare con i vostri casi di vita reale. Ma questo è un caso di decisione del caso, non di una strategia generale. Ad ogni modo, se conosci il tuo codice, il bug e i tuoi refactoring si comportano abbastanza bene per questo, quindi fare uno strumento del genere è fatto in meno di un giorno, probabilmente molto meno.

Con xstream si otterrebbe anche parzialmente questo, ma si dovrebbe modificare l'xml da soli. Se per esempio hai usato db4o dovresti dire che questo e quel campo hanno ora questo e quel nome.

Problemi correlati