Scrivere test per codice il cui scopo non capisco


59

Di recente ho completato un refactoring a scatola nera. Non riesco a registrarlo, perché non riesco a capire come testarlo.

Ad un livello elevato, ho una classe la cui inizializzazione comporta l'acquisizione di valori da una classe B. Se la classe B è "vuota", genera alcune impostazioni predefinite sensate. Ho estratto questa parte in un metodo che inizializza la classe B con le stesse impostazioni predefinite.

Devo ancora capire lo scopo / il contesto di entrambe le classi o il modo in cui verranno utilizzate. Quindi non posso inizializzare l'oggetto da una classe B vuota e controllare che abbia i valori giusti / fa la cosa giusta.

La mia idea migliore è eseguire il codice originale, hardcode nei risultati dei metodi pubblici a seconda dei membri inizializzati, e testare il nuovo codice con quello. Non riesco a capire bene perché mi sento vagamente a disagio con questa idea.

C'è un attacco migliore qui?


28
Sento che hai iniziato dalla parte sbagliata. Dovresti prima capire il codice, quindi testarlo, quindi refactor. Perché stai eseguendo il refactoring senza sapere a cosa serve il codice?
Jacob Raihle,

11
@JacobRaihle È un programma piuttosto specializzato per persone con titoli in cose che non ho mai toccato. Sto raccogliendo il contesto mentre vado, ma semplicemente non è pratico aspettare di avere una solida comprensione prima di iniziare.
JETM,

4
Ciò che non è pratico è riscrivere le cose e, quando i cambiamenti sono in produzione, scoprire perché non dovresti avere. Se prima sarai in grado di testare a fondo , va bene, questo può essere un buon modo per conoscere la base di codice. Altrimenti, è indispensabile che tu capisca prima di cambiare.
Jacob Raihle,

37
Esiste un tipo specifico di test chiamato Test di caratterizzazione per quando si desidera testare il comportamento effettivo del sistema. Devi solo prendere il tuo sistema originale, quindi aggiungere test che affermano tutto ciò che effettivamente fa (e non necessariamente quello che doveva fare!). Servono da impalcature attorno al tuo sistema, che puoi modificare in sicurezza poiché puoi assicurarti che mantenga il suo comportamento.
Vincent Savard,

3
Non puoi chiedere / farti recensire da qualcuno che lo capisce?
pjc50,

Risposte:


122

Stai facendo bene!

La creazione di test di regressione automatizzati è spesso la cosa migliore che è possibile fare per rendere un componente modificabile. Può essere sorprendente, ma tali test possono spesso essere scritti senza la piena comprensione di ciò che il componente fa internamente, purché si comprendano le "interfacce" di input e output (nel significato generale di quella parola). Lo abbiamo fatto diverse volte in passato per applicazioni legacy in piena regola, non solo per le lezioni, e spesso ci ha aiutato a evitare di rompere cose che non comprendevamo appieno.

Tuttavia, è necessario disporre di dati di test sufficienti e assicurarsi di avere una solida conoscenza del comportamento del software dal punto di vista dell'utente di quel componente, altrimenti si rischia di omettere importanti casi di test.

È una buona idea IMHO implementare i test automatici prima di iniziare il refactoring, non in seguito, quindi è possibile eseguire il refactoring in piccoli passaggi e verificare ogni passaggio. Il refactoring stesso dovrebbe rendere il codice più leggibile, quindi ti aiuta ad aumentare la tua comprensione degli interni poco a poco. Quindi i passaggi dell'ordine in questo processo sono

  1. capire il codice "dall'esterno",
  2. scrivere test di regressione,
  3. refactor, che porta a una migliore comprensione degli interni del codice

21
Risposta perfetta, anche esattamente come descritto nel libro "Lavorare con il codice legacy"
Altoyr,

Ho dovuto fare una cosa del genere una volta. Raccogliere i dati di output tipici dall'applicazione prima di modificarli, quindi controllare la mia nuova versione dell'applicazione eseguendo gli stessi dati di test attraverso di essa. 30 anni fa ... Fortran ... Era una specie di elaborazione / mappatura delle immagini, quindi non potevo davvero sapere quale sarebbe stato l'output osservandolo o scrivendo casi di test. E l'ho fatto su un display vettoriale Tektronix (persistente). Lavoro governativo ... 2 Teletipi che mi colpiscono alle spalle.

4
Si potrebbe aggiungere, è ancora possibile scrivere i test per il vecchio codice dopo il fatto. Quindi puoi provarli sulla tua versione refactored e, se ciò si interrompe, esegui una ricerca bisettrice nella cronologia del commit per trovare il punto in cui inizia a rompersi.
CodeMonkey,

2
Suggerirei di fare un'altra cosa. Durante la raccolta dei dati del test, raccogliere le statistiche sulla copertura del codice, se possibile. Saprai quanto bene i tuoi dati di test descrivono il codice in questione.
liori,

2
@nocomprende, È divertente che ho fatto quella cosa esatta con un codice fortran 77 legacy scientifico la scorsa settimana. Aggiungi la stampa dei dati ASCII a un file, imposta le directory di test con gli input e l'output previsto, e il mio caso di test era solo una diff delle due serie di output. Se non corrispondono al personaggio per personaggio, ho rotto qualcosa. Quando il codice è per lo più due subroutine che sono ogni 2-3k LoC, devi iniziare da qualche parte.
Godric Seer,

1

Un motivo importante per la scrittura di unit test è che in qualche modo documentano l'API del componente. Non comprendere lo scopo del codice in fase di test è davvero un problema qui. La copertura del codice è un altro obiettivo importante, difficile da raggiungere senza sapere quali rami di esecuzione esistono e come vengono attivati.

Tuttavia, se è possibile ripristinare lo stato in modo pulito (o costruire ogni volta il nuovo oggetto di test), è possibile scrivere test di tipo "cestino-in-cestino" che alimentano semplicemente input casuali nel sistema e osservano l'output.

Tali test sono difficili da mantenere, poiché quando falliscono, può essere complesso dire perché e quanto sia grave. La copertura può essere discutibile. Tuttavia sono ancora molto meglio di niente. Quando tale test fallisce, lo sviluppatore può rivedere le ultime modifiche con più attenzione e sperare di individuare lì il bug.


1
Qualsiasi tipo di informazione è meglio che volare alla cieca. Individuavo i bug nei programmi server che erano in produzione invocando il debugger su un file di dump di arresto anomalo (Unix) e chiedendo la traccia dello stack. Mi ha dato il nome della funzione in cui si è verificato l'errore. Anche senza altre conoscenze (non sapevo come usare questo debugger) mi ha aiutato in quella che altrimenti sarebbe una situazione misteriosa e irripetibile.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.