2011-11-02 18 views
5

Ho letto tonnellate di articoli, ho visto tonnellate di screencast sul TDD, ma sto ancora cercando di usarlo nel progetto del mondo reale. Il mio problema principale è che non so da dove cominciare, quale test dovrebbe essere il primo. Supponiamo di dover scrivere una libreria client che chiama i metodi del sistema esterno (ad es. Notifica). Voglio questo client per funzionare come segueCome scegliere il punto di partenza di TDD in un progetto del mondo reale?

NotificationClient client = new NotificationClient("abcd1234"); // client ID 
Response code = client.notifyOnEvent(Event.LIMIT_REACHED, 100); // some params of call 

C'è un po 'di traduzione e un messaggio di preparazione formato dietro le quinte, quindi mi piacerebbe di nasconderlo dalle mie applicazioni client.

Non so dove e come iniziare. Devo impostare alcune classi approssimative impostate per questa libreria? Devo iniziare con il test NotificationClient come di seguito

public void testClientSendInvalidEventCommand() { 
    NotificationClient client = new NotificationClient(...); 
    Response code = client.notifyOnEvent(Event.WRONG_EVENT); 
    assertEquals(1223, code.codeValue()); 
} 

Se è così, con una tale prova sono costretto a scrivere implementazione di lavoro completo in una sola volta, senza piccoli passi come TDD stati. Posso prendere in giro il client in client ma poi devo sapere che questa cosa viene derisa in anticipo, quindi ho bisogno di un po 'in anticipo.

Forse dovrei iniziare dal basso, testare prima questo componente di formattazione del messaggio e poi usarlo nel test del client giusto?

Quale strada è quella giusta da percorrere? Dovremmo sempre iniziare dall'alto (come affrontare questo enorme passo richiesto)? Possiamo iniziare con qualsiasi classe realizzando una piccola parte della funzione desiderata (come Formatter in questo esempio)?

Se dovessi sapere dove colpire con i miei test, sarebbe molto più facile per me procedere.

risposta

1

Non confondere lo acceptance tests che si aggancia a ciascuna estremità della propria applicazione e formare un executable specifications con unit tests.

Se si sta eseguendo un TDD "puro", si scrive un test di accettazione che guida i test dell'unità che guidano l'implementazione. testClientSendInvalidEventCommand è il test di accettazione, ma a seconda di quanto siano complicate le cose, delegherete l'implementazione a più classi che potete testare separatamente.

Come sono complicate le cose prima di dover dividerle per testarle e comprenderle correttamente è il motivo per cui si chiama Test Driven Design.

+0

Ma come trovare quelle classi per scrivere test unitari e da dove iniziare? Suppongo di dover impostare alcune classi per poter scegliere il punto di partenza per TDD. Ma allora dove dovrebbe essere questo punto? Dovrebbe essere ai bordi del sistema, o può essere nel mezzo del sistema - come menzionato il Formatter? –

+0

È possibile TDD dal basso verso l'alto o dall'alto verso il basso. Non vedo come il Formatter si inserisce nel tuo esempio. Dato un test, dovresti scrivere la ** quantità minima ** di codice necessaria per farlo passare. –

0

Un obiettivo di TDD è che il test informa il progetto. Quindi il fatto che tu debba pensare a come implementare il tuo NotificationClient è una buona cosa; ti costringe a pensare (auspicabilmente) a semplici astrazioni in primo piano.

Inoltre, il tipo TDD presuppone un refactoring costante. La tua prima soluzione probabilmente non sarà l'ultima; così come perfezionate il vostro codice, i test sono lì per dirvi quali sono le interruzioni, dagli errori di compilazione ai problemi di runtime attuali.

Quindi vorrei semplicemente saltare dentro e iniziare con il test che hai suggerito. Mentre crei mock, dovrai creare dei test per le reali implementazioni di ciò che stai deridendo. Troverai che le cose hanno senso e devono essere sottoposte a refactoring, quindi dovrai modificare i tuoi test man mano che procedi. Questo è il modo in cui dovrebbe funzionare ...

+0

Ok, supponiamo di aver identificato due collaboratori qui: Formatter per la creazione di messaggi nel formato richiesto e ConnectionHandler per la gestione delle connessioni e l'invio di messaggi di basso livello. È sufficiente iniziare i test? Da dove cominciare allora? Con test di ConnectionHandler o Formatter? O forse il cliente con quei due deputati derisi? –

+0

Basta scegliere l'uno o l'altro - penso che tu sia nella paralisi dell'analisi - fallo e si risolverà da solo ... – hvgotcodes

1

Puoi scegliere di lasciare che i test guidino il tuo design dal basso verso l'alto o dall'alto verso il basso. Entrambi funzionano bene per diversi sviluppatori in diverse situazioni.Entrambi gli approcci costringeranno a prendere alcune di quelle decisioni progettuali "in anticipo", ma questa è una buona cosa. Prendere queste decisioni per scrivere i tuoi test è un design basato sui test!

Nel tuo caso, hai un'idea di quale sia l'interfaccia esterna di alto livello per il sistema che stai sviluppando, quindi cominciamo da lì. Scrivi un test per capire come pensi che gli utenti del tuo client di notifica debbano interagire con esso e lasciarlo fallire. Questo test è la base per i test di accettazione o integrazione e continueranno a fallire fino a quando le caratteristiche descritte non saranno terminate. Va bene. Ora abbassa un livello. Quali sono i passaggi che devono verificarsi per fornire quell'interfaccia di alto livello? Possiamo scrivere un test di integrazione o unità per quei passaggi? Hanno delle dipendenze che non hai considerato che potrebbero causare la modifica dell'interfaccia del centro notifiche che hai iniziato a definire? Continuare a definire il comportamento in profondità prima di definire i test non riuscite finché non si è effettivamente raggiunto un test unitario. Ora attuare abbastanza per superare quel test unitario e continuare. Prendi i test unitari che passano finché non hai costruito abbastanza per superare un test di integrazione e così via. Alla fine avrai completato una costruzione in profondità di un albero di test e dovresti avere una funzionalità ben collaudata il cui design è stato guidato dai tuoi test.

2

mi piacerebbe iniziare con questa linea:

NotificationClient client = new NotificationClient("abcd1234"); // client ID 

Suona come abbiamo bisogno di una NotificationClient, che ha bisogno di un ID cliente. È una cosa facile da testare. Il mio primo test potrebbe essere simile:

public void testNewClientAbcd1234HasClientId() { 
    NotificationClient client = new NotificationClient("abcd1234"); 
    assertEquals("abcd1234", client.clientId()); 
} 

Naturalmente, non si compila in un primo momento - non prima avevo scritto una classe NotificationClient con un costruttore che prende un parametro di stringa e un clientId() metodo che restituisce una stringa, ma fa parte del ciclo TDD.

public class NotificationClient { 
    public NotificationClient(string clientId) { 
    } 
    public string clientId() { 
     return ""; 
    } 
} 

A questo punto, posso correre la mia prova e guardare fallire (perché ho clientId hard-coded (s ') ritorno a essere una stringa vuota). Una volta che ho avuto la mia mancanza di test di unità, scrivo quel tanto che basta il codice di produzione (in NotificationClient) per ottenere la prova da superare:

public string clientId() { 
     return "abcd1234"; 
    } 

Ora tutti i miei test passano, in modo da poter prendere in considerazione che cosa fare dopo. L'ovvio (beh, ovvio per me ) prossimo passo è quello di fare in modo che posso creare clienti i cui ID non è "abcd1234":

public void testNewClientBcde2345HasClientId() { 
    NotificationClient client = new NotificationClient("bcde2345"); 
    assertEquals("bcde2345", client.clientId()); 
} 

eseguo il mio suite di test e osservo che testNewClientBcde2345HasClientId() non riesce mentre testNewClientAbcd1234HasClientId() passa, e ora ho un buon motivo per aggiungere una variabile membro alla NotificationClient:

public class NotificationClient { 
    private string _clientId; 
    public NotificationClient(string clientId) { 
     _clientId = clientId; 
    } 
    public string clientId() { 
     return _clientId; 
    } 
} 

Supponendo errori tipografici sono intrufolato in, che otterrà tutte le mie prove da superare, e mi può passare a qualunque sia il prossimo passo. (Nel tuo esempio, probabilmente verificherebbe che notifyOnEvent(Event.WRONG_EVENT) restituisca un Response il cui codeValue() è uguale a 1223.)

Questo può essere d'aiuto?

Problemi correlati