2009-02-06 17 views
11

Avendo appena letto i primi quattro capitoli di Refactoring: Improving the Design of Existing Code, ho intrapreso il mio primo refactoring e quasi immediatamente sono arrivato a un posto di blocco. Deriva dal requisito che prima di iniziare il refactoring, dovresti sottoporre i test unitari al codice precedente. Ciò ti consente di essere sicuro che il tuo refactoring non ha modificato il codice originale (solo come lo ha fatto).Rifacimento pratico con test unitari

Quindi la mia prima domanda è questa: come faccio a testare un metodo in un codice legacy? Come posso mettere un test unitario attorno a un metodo di 500 linee (se sono fortunato) che non fa un solo compito? Mi sembra che dovrei refactoring il mio codice legacy solo per renderlo unit-testabile.

Qualcuno ha esperienza di refactoring utilizzando i test unitari? E se sì, hai qualche esempio pratico che puoi condividere con me?

La mia seconda domanda è piuttosto difficile da spiegare. Ecco un esempio: voglio refactoring un metodo legacy che popola un oggetto da un record del database. Non dovrei scrivere un test unitario che paragona un oggetto recuperato usando il vecchio metodo, con un oggetto recuperato usando il mio metodo refactored? Altrimenti, come potrei sapere che il mio metodo refactored produce gli stessi risultati del vecchio metodo? Se è vero, allora quanto tempo lascerò il vecchio metodo deprecato nel codice sorgente? Lo faccio appena dopo aver provato alcuni record diversi? O, devo tenerlo in giro per un po 'nel caso in cui si verifichi un bug nel mio codice refactored?

Infine, poiché un paio di persone hanno chiesto ... il codice legacy è stato originariamente scritto in VB6 e quindi trasferito su VB.NET con modifiche minime dell'architettura.

+0

Ottima domanda. Puoi anche provare Katas, che ti aiuta a prendere l'abitudine di scrivere un buon codice e come scrivere codice unitario testato: https://github.com/garora/TDD-Katas –

risposta

4

Buon esempio di teoria che incontra la realtà. I test unitari hanno lo scopo di testare una singola operazione e molti puristi del modello insistono su Single Responsibilty, quindi abbiamo un bel codice pulito e test per utilizzarlo. Tuttavia, nel mondo reale (disordinato), il codice (in particolare il codice legacy) fa un sacco di cose e non ha test. Ciò di cui ha bisogno è una dose di refactoring per pulire il casino.

Il mio approccio è di costruire test, utilizzando gli strumenti Unit Test, che testano molte cose in un singolo test. In un test, posso verificare se la connessione DB è aperta, modificare molti dati e fare una verifica prima/dopo sul DB. Mi trovo inevitabilmente a scrivere classi di helper per fare il controllo, e il più delle volte quegli helper possono essere aggiunti alla base del codice, poiché hanno incapsulato comportamenti/logica/requisiti emergenti. Non voglio dire di avere un solo test enorme, quello che intendo è che i test stanno facendo un lavoro che un purista chiamerebbe un test di integrazione - esiste ancora una cosa del genere? Inoltre ho trovato utile creare un modello di test e quindi creare molti test da esso, per controllare condizioni al contorno, elaborazione complessa, ecc.

BTW di quale ambiente linguistico stiamo parlando? Alcune lingue si prestano al refactoring meglio di altre.

+0

Spesso mi trovo a discutere per un compromesso sul lato pragmatico di una discussione purista come questa. Forse sto solo invecchiando e non ho più la lotta in me per guidare la carica di purista. ;) – JMD

+0

@ JMD.Anche di più, l'unico test unitario significativo per il refactoring è un test funzionale unitario end-to-end con una dipendenza minima dalla struttura interna. Allo stesso tempo nessuno ne parla. La mia ipotesi è che ci sia un piccolo refactoring in corso: il codice esistente è così cattivo che nella maggior parte dei casi il refactoring è fatto sostituendo le unità funzionali. – zzz777

0

Questo è davvero uno dei problemi chiave nel tentativo di ripristinare il codice legacy. Sei in grado di suddividere il dominio del problema in qualcosa di più granulare? Quel metodo con più di 500 linee fa qualcosa di diverso dalle chiamate di sistema ai JAR/DLL/assiemi JDK/Win32/.NET Framework? Cioè Ci sono chiamate di funzione più granulari all'interno di quel colosso di 500+ linee che potresti testare unitamente?

+0

In effetti, ci sono funzioni più granulari ma come posso testare le unità senza prima refactoring il codice legacy per estrarre i metodi più granulari? –

+0

Questa è la domanda da $ 64.000. E a volte la risposta è un compromesso. Vuoi sforzarti per un refactoring perfetto, ma a volte devi scrivere quali test unitari puoi/mentre/stai migliorando il codice legacy. Almeno questa è stata la mia esperienza. – JMD

1

Dalla mia esperienza, scriverei test non per metodi particolari nel codice legacy, ma per la funzionalità generale che fornisce. Questi potrebbero o potrebbero non essere strettamente correlati ai metodi esistenti.

1

Scrivere prove su qualsiasi livello del sistema (se è possibile), se ciò significa eseguire un database ecc.Avrai bisogno di scrivere molto più codice per affermare ciò che il codice sta facendo attualmente, poiché un metodo 500 line + potrebbe avere un sacco di comportamenti racchiusi in esso. Per quanto riguarda il confronto tra il vecchio e il nuovo, se si scrivono i test rispetto al vecchio codice, passano e coprono tutto ciò che fa, quindi quando li si esegue contro il nuovo codice si sta effettivamente controllando il vecchio rispetto al nuovo. Ho fatto questo per testare un trigger sql complesso che volevo refactoring, è stato un dolore e ha richiesto tempo, ma un mese dopo, quando abbiamo trovato un altro problema in quell'area, valeva la pena fare test su cui fare affidamento.

9

Per istruzioni su come refactoring codice legacy, si potrebbe voler leggere il libro Working Effectively with Legacy Code. C'è anche una breve versione PDF disponibile here.

+0

Ottimo per avere il link al PDF - bel lavoro! – MarkJ

+0

+1 Questo libro risponde esattamente alla domanda. –

1

Nella mia esperienza questa è la realtà quando si lavora su codice Legacy. Book (Working with Legacy ..) menzionato da Esko è un lavoro eccellente che descrive vari approcci che possono portarvi lì.

Ho visto problemi simili con lo stesso unit test che è diventato un test di sistema/funzionale. La cosa più importante per sviluppare test per Legacy o codice esistente è definire il termine "unità". Può essere anche un'unità funzionale come "leggere dal database" ecc. Identificare le unità funzionali chiave e mantenere i test che aggiungono valore.

Per inciso, ci sono stati discorsi recenti tra Joel S. e Martin F. su TDD/unit-test. La mia opinione è che è importante definire l'unità e concentrarsi su di essa! URL: Open Letter, Joel's transcript e podcast

0

Il seguente libro: The Art of Unit Testing contiene un paio di capitoli con alcune idee interessanti su come trattare con il codice legacy in termini di sviluppo di unit test.

L'ho trovato abbastanza utile.