Come rimuovere una funzione o funzione quando si usa TDD

20

Nei testi su TDD ho letto spesso "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 C con metodi a() e b() . Ora penso che sarebbe bello avere un metodo f() che è guidato in C . Infatti f() sostituisce tutte le chiamate a b() con l'eccezione dei test unitari che hanno definito / descritto b() . Non è più necessario, tranne per i test.

È sufficiente salvare b() e tutti i test che lo hanno utilizzato? È quella parte di "migliorare la leggibilità"?

    
posta TobiMcNamobi 24.05.2016 - 10:01
fonte

6 risposte

16

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

Ciò detto, il refactoring generalmente significa migliorare il codice senza modificarne il comportamento. Se pensi a qualcosa che migliora il codice, fallo e basta. Non è necessario installarlo in qualche buca prima che tu possa farlo.

    
risposta data 24.05.2016 - 10:03
fonte
27

La rimozione di un metodo pubblico non è un "refactoring": il refactoring sta cambiando l'implementazione mentre continua a superare i test esistenti.

Tuttavia, la rimozione di un metodo non necessario è un cambio di progetto perfettamente ragionevole.

TDD lo disegna in una certa misura, perché nella revisione dei test, si può osservare che sta testando un metodo non necessario. I test guidano il tuo progetto, perché puoi andare "Guarda, questo test non ha nulla a che fare con il mio obiettivo".

Potrebbe rivelarsi di più a livelli più alti di test, in combinazione con gli 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 un metodo non viene utilizzato. L'analisi del codice statico può anche indicare che i metodi non sono utilizzati.

Ci sono due approcci per rimuovere un metodo; entrambi funzionano in diverse circostanze:

  1. Elimina il metodo. Seguire gli errori di compilazione, per eliminare qualsiasi codice dipendente e test. Se sei soddisfatto che i test interessati siano eliminabili, invia le modifiche. In caso contrario, esegui il rollback.

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

(Ciò presuppone che la tua suite di test abbia una buona copertura per iniziare)

    
risposta data 24.05.2016 - 12:14
fonte
10

In fact f() replaces all calls to b() with the exception of the unit tests that defined / described b()

IMHO il tipico ciclo TDD sarà simile a questo:

  • scrivere test falliti per f () (probabilmente basato sui test per b ()): test go red

  • implementa f () - > i test diventano verdi

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

Per l'ultimo passaggio, potresti prendere in considerazione di rimuovere b () per primo e vedere cosa succede (quando usi un linguaggio compilato, il compilatore dovrebbe lamentarsi solo dei test esistenti, quando no, i vecchi test unitari per b falliranno, quindi è chiaro che devi rimuoverli anche tu.

    
risposta data 24.05.2016 - 11:30
fonte
4

Sì, lo è.

Il codice migliore, più privo di bug e più leggibile è il codice che non esiste. Cerca di scrivere il maggior numero possibile di codice senza rispettare le tue esigenze.

    
risposta data 24.05.2016 - 10:03
fonte
2

È preferibile rimuovere b() una volta che non è più utilizzato, per lo stesso motivo per cui è preferibile non aggiungere funzioni non utilizzate in primo luogo. Che tu lo chiami "leggibilità" o qualcos'altro, a parità di tutti gli altri è un miglioramento del codice che non contiene nulla per cui non serve. Per avere almeno una misura specifica con la quale è meglio non averlo, rimuoverlo garantisce che il suo costo di mantenimento futuro dopo tale modifica è pari a zero!

Non ho trovato alcuna tecnica speciale necessaria per rimuoverla effettivamente con i suoi test, dal momento che qualsiasi idea di sostituire b() con qualcosa di nuovo deve ovviamente essere accompagnata da una considerazione di tutto il codice che attualmente chiama b() , e i test sono un sottoinsieme di "tutto il codice".

La linea di ragionamento che generalmente funziona per me è quella al punto in cui ho notato che f() ha reso b() obsoleto, quindi b() dovrebbe essere almeno deprecato e sto cercando di trovare tutte le chiamate a b() con l'intento di sostituirli con chiamate a f() , Considero anche il codice di prova . In particolare, se b() non è più necessario, posso e dovrei rimuovere i suoi test unitari.

Hai ragione a dire che mi mi impone di notare che b() non è più necessario. Questa è una questione di abilità (e, come dice slim, rapporti sulla copertura del codice nei test di livello superiore). Se solo i test unitari, e nessun test funzionale, si riferiscono a b() , allora posso essere cautamente ottimista sul fatto che non fa parte di alcuna interfaccia pubblicata e quindi rimuoverlo non è un cambiamento di rottura per qualsiasi codice non sotto il mio controllo diretto.

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

Penso che in pratica la maggior parte delle persone permetta di effettuare una certa riprogettazione con un ciclo rosso / verde / refactoring, o ritengono che rimuovere i test unitari ridondanti sia una parte valida di un "refactoring" anche se in senso stretto non si tratta di refactoring. La tua squadra può decidere quanto dramma e documenti dovrebbero essere coinvolti nel giustificare questa decisione.

Ad ogni modo, se b() era importante, c'erano test funzionali per questo, e quelli non sarebbero stati rimossi leggermente, ma hai già detto che ci sono solo test unitari. Se non si distingue correttamente tra test unitari (scritti nella progettazione interna corrente del codice, che è stata modificata) e test funzionali (scritti su interfacce pubblicate, che forse non si desidera modificare), è necessario essere più cauti sulla rimozione dei test unitari.

    
risposta data 25.05.2016 - 01:03
fonte
2

Una cosa che cerco sempre di ricordare è che ora stiamo usando CODE REPOSITORIES con VERSION CONTROL. Quel codice cancellato non è in realtà andato ... è ancora lì da qualche parte in una precedente iterazione. Quindi buttalo via! Sii liberale con il tasto Canc, perché puoi sempre tornare indietro e recuperare quel prezioso ed elegante metodo che pensavi potesse essere utile un giorno ... se mai un giorno dovesse arrivare. È lì.

Ovviamente ciò va di pari passo con l'avvertenza sui mali e il pericolo di rilasci non retrocompatibili ... applicazioni esterne basate sull'implementazione dell'interfaccia, ora orfane dal codice (improvvisamente) deprecato.

    
risposta data 25.05.2016 - 02:29
fonte

Leggi altre domande sui tag