Come ottenere correttamente l'API iniziale usando TDD?

12

Questa potrebbe essere una domanda piuttosto stupida come lo sono nei miei primi tentativi di TDD. Ho amato il senso di confidenza che porta e in generale la migliore struttura del mio codice, ma quando ho iniziato ad applicarlo su qualcosa di più grande di esempi di giocattoli di una classe, mi sono imbattuto in difficoltà.

Supponiamo che tu stia scrivendo una sorta di libreria. Sai cosa deve fare, sai un modo generale di come dovrebbe essere implementato (architettura saggio), ma continui a "scoprire" che hai bisogno di apportare modifiche alla tua API pubblica mentre esegui il codice. Forse hai bisogno di trasformare questo metodo privato in un modello di strategia (e ora devi passare una strategia derisa nei tuoi test), forse hai smarrito una responsabilità qua e là e dividi una classe esistente.

Quando stai migliorando il codice esistente, TDD sembra un ottimo adattamento, ma quando scrivi tutto da zero, l'API per cui scrivi i test è un po '"sfocata" a meno che tu non faccia un grande progetto in anticipo. Che cosa fai quando hai già 30 test sul metodo che ha la sua firma (e per quella parte, il comportamento) è cambiato? Questo è un sacco di test da cambiare una volta che si sommano.

    
posta Vytautas Mackonis 07.12.2012 - 21:06
fonte

4 risposte

12

Ciò che chiami "grande design in primo piano" chiamo "pianificazione ragionevole della tua architettura di classe."

Non puoi far crescere un'architettura dai test unitari. Anche Uncle Bob dice che .

If you're not thinking through the architecture, if what you're doing instead is ignoring architecture and throwing tests together and getting them to pass, you're destroying the thing that will allow the building to stay up because it's the concentration on the structure of the system and solid design decisions that helped the system maintain its structural integrity.

link , pagina 4

Penso che sarebbe più sensato avvicinarsi a TDD da una prospettiva di convalidare il tuo progetto strutturale. Come sai che il design è sbagliato se non lo test? E come verificate che le vostre modifiche siano corrette senza cambiare anche i test originali?

Il software è "soft" proprio perché è soggetto a modifiche. Se sei a disagio per la quantità di modifiche, continua ad acquisire esperienza nella progettazione architettonica e il numero di modifiche che dovrai apportare alle tue architetture applicative diminuirà nel tempo.

    
risposta data 07.12.2012 - 21:35
fonte
3

Se fai TDD. Non è possibile modificare la firma e il comportamento senza averlo guidato dai test. Quindi i 30 test che falliscono sono stati eliminati nel processo o modificati / refactored insieme al codice. Oppure sono ormai obsoleti, sicuri da eliminare.

Non puoi ignorare le 30 volte in rosso nel tuo ciclo refactore rosso-verde?

I tuoi test dovrebbero essere refactored insieme al tuo codice di produzione. Se puoi permetterti, riesegui tutti i test dopo ogni modifica.

Non aver paura di eliminare i test TDD. Alcuni test finiscono per testare i blocchi costitutivi per ottenere il risultato desiderato. Il risultato desiderato a livello funzionale è ciò che conta. I test sui passaggi intermedi dell'algoritmo che hai scelto / inventato possono o non possono avere molto valore quando c'è più di un modo per raggiungere il risultato o inizialmente sei finito in un vicolo cieco.

A volte puoi creare dei test di integrazione decenti, tenerli ed eliminare il resto. Dipende in qualche modo se lavori al rovescio o dall'alto e quanti passi hai fatto.

    
risposta data 08.12.2012 - 03:08
fonte
1

Come ha appena detto Robert Harvey, probabilmente stai cercando di usare TDD per qualcosa che dovrebbe essere gestito da uno strumento concettuale diverso (vale a dire: "design" o "modellazione").

Prova a progettare (o "modellare") il tuo sistema in un modo abbastanza astratto ("generale", "vago"). Ad esempio, se devi modellare una macchina, basta avere una classe di macchina con qualche metodo e campo vaghi, come startEngine () e posti int. Cioè: descrivi ciò che vuoi esporre al pubblico , non come vuoi implementarlo. Cerca di esporre solo le funzionalità di base (leggi, scrivi, avvia, interrompi, ecc.) E lascia che il codice client venga elaborato su di esso (prepareMyScene (), killTheEnemy (), ecc.)

Annota i tuoi test assumendo questa semplice interfaccia pubblica.

Cambia il comportamento interno delle tue classi e dei tuoi metodi ogni volta che ne hai bisogno.

Se e quando è necessario cambiare la tua interfaccia pubblica e la tua suite di test, fermati e pensa. Molto probabilmente questo è un segno che c'è qualcosa di sbagliato nella tua API e nella tua progettazione / modellazione.

Non è raro cambiare un'API. La maggior parte dei sistemi nella versione 1.0 avvertono esplicitamente i programmatori / utenti da eventuali modifiche nella loro API. Ciononostante, un flusso continuo e incontrollato di modifiche API è un chiaro segno di cattivo (o totalmente mancante) design.

BTW: di solito dovresti avere solo una manciata di test per metodo. Un metodo, per definizione, dovrebbe implementare una "azione" chiaramente definita su alcuni tipi di dati. In un mondo perfetto, questa dovrebbe essere una singola azione che corrisponde a un singolo test. Nel mondo reale non è insolito (e non sbagliato) avere poche "versioni" diverse della stessa azione e pochi test corrispondenti diversi. Di sicuro, dovresti evitare di avere 30 test con lo stesso metodo. Questo è un chiaro segno che il metodo cerca di fare troppo (e il suo codice interno è cresciuto senza controllo).

    
risposta data 08.12.2012 - 12:45
fonte
0

Lo guardo dal punto di vista dell'utente. Ad esempio, se le tue API mi consentono di creare un oggetto Person con nome ed età, è preferibile un metodo di costruzione Person (nome stringa, int age) e accessor per nome ed età. È semplice creare casi di test per nuove persone con e senza nome e età.

Doug

    
risposta data 08.12.2012 - 03:33
fonte

Leggi altre domande sui tag