Come rimuovere una funzione o caratteristica quando si utilizza TDD


20

Nei testi su TDD leggo spesso di "rimuovere la duplicazione" o "migliorare la leggibilità" durante la fase di refactoring. Ma cosa mi fa rimuovere una funzione inutilizzata?

Ad esempio diciamo che esiste una classe Ccon metodi a()e b(). Ora penso che sarebbe bello avere un metodo f()che è guidato C. f()Sostituisce infatti tutte le chiamate a b()eccezione delle unit test definite / descritte b(). Non è più necessario, ad eccezione dei test.

Si salva solo per rimuovere b()e tutti i test che lo hanno utilizzato? Fa parte di "migliorare la leggibilità"?


3
Basta aggiungere un altro test per verificare che la funzione non esista, e poi andare a correggere i casi di errore;)
Filip Haglund

@FilipHaglund A seconda della lingua questo potrebbe essere possibile. Ma come documentazione del codice sembrerebbe strano.
TobiMcNamobi,

1
Solo per chiunque non lo sapesse meglio: il commento di @ FilipHaglund è ovviamente uno scherzo. Non farlo.
jhyot,

Risposte:


16

Sì, naturalmente. Il codice più semplice da leggere è quello che non c'è.

Detto questo, refactoring in genere significa migliorare il codice senza modificarne il comportamento. Se pensi a qualcosa che migliora il codice, fallo e basta. Non è necessario inserirlo in un buco di piccione prima che ti sia permesso farlo.


@ jpmc26 Agile, amico, agile! :-)
TobiMcNamobi

1
"Il codice più semplice da leggere è quello che non c'è." - Sto appendendo quella citazione al muro: D
winkbrace,

27

La rimozione di un metodo pubblico non è "refactoring": il refactoring sta modificando l'implementazione pur continuando a superare i test esistenti.

Tuttavia, la rimozione di un metodo non necessario è una modifica di progettazione perfettamente ragionevole.

TDD lo elabora in una certa misura, perché nel riesaminare i test, potresti notare che sta testando un metodo non necessario. I test stanno guidando il tuo progetto, perché puoi andare "Guarda, questo test non ha nulla a che fare con il mio obiettivo".

Può rivelarsi più a livelli più alti di test, in combinazione con strumenti di copertura del codice. Se si eseguono test di integrazione con copertura del codice e si vede che i metodi non vengono chiamati, è un indizio che non viene utilizzato un metodo. L'analisi del codice statico può anche indicare che i metodi non sono utilizzati.

Esistono due approcci per rimuovere un metodo; entrambi lavorano in circostanze diverse:

  1. Elimina il metodo Seguire gli errori di compilazione, per eliminare qualsiasi codice dipendente e test. Se sei soddisfatto che i test interessati siano usa e getta, esegui il commit delle modifiche. In caso contrario, tornare indietro.

  2. Elimina i test che ritieni obsoleti. Esegui l'intera suite di test con copertura del codice. Elimina i metodi che non sono stati esercitati dalla suite di test.

(Ciò presuppone che la suite di test abbia una buona copertura per cominciare).


10

Infatti f () sostituisce tutte le chiamate a b () ad eccezione dei test unitari che hanno definito / descritto b ()

IMHO il tipico ciclo TDD sarà simile al seguente:

  • scrivere test non riusciti per f () (probabilmente basato sui test per b ()): i test diventano rossi

  • implementare f () -> i test diventano verdi

  • refactor : -> rimuovi b () e tutti i test per b ()

Per l'ultimo passaggio, potresti considerare di rimuovere prima b () e vedere cosa succede (quando si utilizza un linguaggio compilato, il compilatore dovrebbe lamentarsi solo dei test esistenti, altrimenti i vecchi test unitari per b falliranno, quindi è chiaro che anche tu devi rimuoverli).


4

Sì.

Il codice migliore, più privo di bug e più leggibile è il codice che non esiste. Sforzati di scrivere il maggior numero possibile di non-codice nel rispetto delle tue esigenze.


9
Hai perso la parte "come fare".
JeffO,

2

È preferibile rimuoverlo b()quando non viene più utilizzato, per lo stesso motivo per cui è preferibile non aggiungere funzioni non utilizzate in primo luogo. Sia che tu lo chiami "leggibilità" o qualcos'altro, a parità di condizioni è un miglioramento del codice che non contiene nulla per cui non è utile. Per avere almeno una misura specifica con la quale è meglio non averla, rimuoverla garantisce che i suoi futuri costi di manutenzione dopo quella modifica siano pari a zero!

Non ho trovato alcuna tecnica speciale necessaria per rimuoverlo effettivamente con i suoi test, dal momento che ogni pensiero di sostituirlo b()con qualcosa di nuovo deve ovviamente essere accompagnato da una considerazione di tutto il codice attualmente chiamato b(), e i test sono un sottoinsieme di "tutto il codice ".

Il ragionamento che generalmente funziona per me è che nel momento in cui noto che f()è diventato b()obsoleto, quindi b()dovrebbe essere almeno deprecato, e sto cercando di trovare tutte le chiamate b()con l'intenzione di sostituirle con chiamate a f(), I considerare anche il codice di prova . In particolare, se b()non è più necessario, allora posso e dovrei rimuovere i suoi test unitari.

Hai perfettamente ragione sul fatto che nulla mi costringe a notare che b()non è più necessario. Questa è una questione di abilità (e, come dice slim, riporta rapporti di copertura del codice su test di livello superiore). Se si riferiscono solo a test unitari e nessun test funzionale, b()posso essere cautamente ottimista sul fatto che non fa parte di alcuna interfaccia pubblicata e quindi rimuoverlo non è una modifica sostanziale per qualsiasi codice non sotto il mio controllo diretto.

Il ciclo rosso / verde / refactor non menziona esplicitamente la rimozione dei test. Inoltre, la rimozione b()viola il principio aperto / chiuso poiché chiaramente il componente è aperto per la modifica. Quindi, se vuoi pensare a questo passaggio come qualcosa al di fuori del semplice TDD, vai avanti. Ad esempio, potresti avere un processo per dichiarare "cattivo" un test, che può essere applicato in questo caso per rimuovere il test in base al test per qualcosa che non dovrebbe essere lì (la funzione non necessaria b()).

Penso che in pratica la maggior parte delle persone probabilmente consenta di eseguire una certa quantità di riprogettazione insieme a un ciclo rosso / verde / refattore, o considerano la rimozione di test unitari ridondanti una parte valida di un "refattore" anche se in senso stretto non è refactoring. Il tuo team può decidere quanto dramma e scartoffie dovrebbero essere coinvolti nel giustificare questa decisione.

Ad ogni modo, se b()fosse importante, ci sarebbero test funzionali per esso, e quelli non verrebbero rimossi alla leggera, ma hai già detto che ci sono solo test unitari. Se non si distingue correttamente tra unit test (scritti nell'attuale design interno del codice, che è stato modificato) e test funzionali (scritti in interfacce pubblicate, che forse non si desidera modificare), è necessario essere più cauti sulla rimozione dei test unitari.


2

Una cosa da cercare sempre di ricordare è che ora stiamo usando REPOSITORI DI CODICE con CONTROLLO VERSIONE. Quel codice eliminato non è effettivamente sparito ... è ancora lì da qualche parte in una precedente iterazione. Quindi spazzalo via! Sii liberale con il tasto Canc, perché puoi sempre tornare indietro e recuperare quel prezioso metodo elegante che pensavi potesse essere utile un giorno ... se un giorno dovesse mai accadere. È lì.

Ovviamente, ciò si accompagna alla diffidenza dei mali e al pericolo di versioni non compatibili con le versioni precedenti ... applicazioni esterne che si basavano sull'implementazione dell'interfaccia, che ora sono rese orfane dal tuo codice (improvvisamente) deprecato.


L'eliminazione del codice non è un problema in generale. Mi piace da eliminare per cancellare le righe, le funzioni, o ... beh, interi moduli! Se possibile. E lo faccio tutto con o senza TDD. Ma: esiste un punto nel flusso di lavoro TDD in cui il codice (non dupli) viene eliminato? In caso contrario, verrà comunque eliminato. Ma c'è? Questa era la mia domanda.
TobiMcNamobi,
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.