Non capisco come TDD mi aiuti a ottenere un buon design se ho bisogno di un progetto per iniziare a testarlo

49

Sto cercando di avvolgere la mia mente su TDD, in particolare sulla parte di sviluppo. Ho esaminato alcuni libri, ma quelli che ho trovato riguardano principalmente la parte di test: la cronologia di NUnit, perché i test sono buoni, Red / Green / Refactor e come creare un calcolatore di stringhe.

Roba buona, ma quella è "solo" Unit Testing, non TDD. Nello specifico, non capisco come TDD mi aiuti a ottenere un buon design se ho bisogno di un Design per iniziare a testarlo.

Per illustrare, immagina questi 3 requisiti:

  • Un catalogo deve avere un elenco di prodotti
  • Il catalogo dovrebbe ricordare quali prodotti sono stati visualizzati da un utente
  • Gli utenti dovrebbero essere in grado di cercare un prodotto

A questo punto, molti libri estraggono un coniglio magico da un cappello e si immergono semplicemente in "Test del ProductService", ma non spiegano in che modo sono giunti alla conclusione che esiste un ProductService in primo luogo. Questa è la parte "Sviluppo" in TDD che sto cercando di capire.

Ci deve essere un progetto esistente, ma roba al di fuori dei servizi di entità (cioè: c'è un prodotto, quindi ci dovrebbe essere un ProductService) non è stato trovato da nessuna parte (ad esempio, il secondo requisito richiede che io abbia un po 'di concetto di un utente, ma dove dovrei mettere la funzionalità da ricordare? E cercare una funzionalità del ProductService o un SearchService separato? Come faccio a sapere quale scegliere?)

Secondo SOLID , avrei bisogno di un UserService, ma se progetto un sistema senza TDD, potrei finire con un sacco di servizi a singolo metodo. TDD non è destinato a farmi scoprire il mio design in primo luogo?

Sono uno sviluppatore .net, ma anche le risorse Java funzionerebbero. Sento che non sembra esserci una vera applicazione di esempio o un libro che si occupa di una vera linea di applicazione aziendale. Qualcuno può fornire un chiaro esempio che illustra il processo di creazione di un disegno usando TDD?

    
posta Michael Stum 29.05.2013 - 19:29
fonte

11 risposte

17

L'idea di TDD è iniziare con i test e lavorare da quello. Quindi, per fare il tuo esempio di "Un catalogo deve avere un elenco di prodotti" potrebbe essere visto come avere un test di "Controlla i prodotti in catalogo" e quindi questo è il primo test. Ora, cosa contiene un catalogo? Cosa contiene un prodotto? Questi sono i prossimi pezzi e l'idea è di mettere insieme alcuni pezzi che potrebbero essere qualcosa come un ProductService che nascerà dall'ottenere quel primo test da superare.

L'idea di TDD è iniziare con un test e poi scrivere il codice che fa passare quel test come primo punto. I test unitari fanno parte di questo sì, ma non si sta guardando l'immagine complessiva che si forma iniziando con i test e quindi scrivendo il codice in modo che non ci siano punti ciechi a questo punto poiché non c'è ancora alcun codice.

Test Driven Development Tutorial dove le diapositive 20-22 sono le più importanti. L'idea è sapere che cosa dovrebbe fare la funzionalità di conseguenza, scrivere un test per questo e quindi creare una soluzione. La parte di progettazione varierà in quanto a seconda di ciò che è richiesto potrebbe non essere così semplice da fare. Un punto chiave è usare TDD dall'inizio piuttosto che provare a introdurre tardi in un progetto. Se inizi prima con i test, questo può aiutare ed è probabilmente degno di nota in un certo senso. Se provi ad aggiungere i test più tardi, diventa qualcosa che può essere rimandato o ritardato. Anche le diapositive successive potrebbero essere utili.

Un vantaggio principale di TDD è che, iniziando con i test, non sei inizialmente bloccato in un progetto. Quindi, l'idea è di costruire i test e creare il codice che supererà quei test come metodologia di sviluppo. Un Big Design Up Front può causare problemi poiché questo dà l'idea di bloccare le cose in posizione che rende il sistema in costruzione meno agile alla fine.

Robert Harvey ha aggiunto questo nei commenti che vale la pena affermare nella risposta:

Unfortunately I think that this is a common misconception about TDD: you can't grow a software architecture by just writing unit tests and making them pass. Writing unit tests does influence the design, but it doesn't create the design. You have to do that.

    
risposta data 29.05.2013 - 19:43
fonte
8

Per quello che vale, TDD mi aiuta a presentare il miglior design più rapidamente che non a TDD. Probabilmente sarei arrivato al miglior design con o senza di esso. Ma quella volta che avrei passato a rifletterci sopra e a fare qualche pugnalata al codice, è stato speso invece a scrivere dei test. Ed è meno tempo. Per me. Non per tutti. E, anche se richiedesse la stessa quantità di tempo, mi lascerebbe con una serie di test, in modo tale che il refactoring sarebbe più sicuro, portando a un codice ancora migliore lungo la linea.

Come si fa?

In primo luogo, mi incoraggia a pensare a ogni classe come un servizio ad un codice client. Un codice migliore deriva dal pensare a come il codice chiamante vuole utilizzare l'API piuttosto che preoccuparsi di come dovrebbe apparire il codice stesso.

In secondo luogo, mi impedisce di scrivere troppa complessità ciclometrica in un unico metodo, mentre ci sto pensando. Ogni percorso extra attraverso un metodo tende a raddoppiare il numero di test che devo fare. La pura pigrizia impone che dopo aver aggiunto troppa logica e aver scritto 16 test per aggiungere una condizione, è ora di estrarne un po 'in un altro metodo / classe e testarlo separatamente.

È davvero così semplice. Non è uno strumento di design magico.

    
risposta data 30.05.2013 - 00:24
fonte
6

I'm trying to wrap my head around TDD... To illustrate, imagine these 3 requirements:

  • A catalog needs to have a list of products
  • The catalog should remember which products a user viewed

Questi requisiti dovrebbero essere ridefiniti in termini umani. Chi vuole sapere quali prodotti l'utente ha precedentemente visualizzato? L'utente? Un venditore?

  • Users should be able to search for a product

Come? Per nome? Per marca? Il primo passo nello sviluppo basato su test è la definizione di un test, ad esempio:

browse to http://ourcompany.com
enter "cookie" in the product search box
page should show "chocolate-chip cookies" and "oatmeal cookies"

>

At this points, many books pull a magic rabbit out of a hat and just dive into "Testing the ProductService", but they don't explain how they came to the conclusion that there is a ProductService in the first place.

Se questi sono gli unici requisiti, certamente non salto per creare un ProductService. Potrei creare una pagina Web molto semplice con un elenco di prodotti statici. Ciò funzionerebbe perfettamente fino ad arrivare ai requisiti per aggiungere ed eliminare prodotti. A quel punto potrei decidere che è più semplice utilizzare un database relazionale e un ORM e creare una classe Product mappata a una singola tabella. Ancora nessun ProductService. Classi come ProductService verranno creati quando e se sono necessari. Potrebbero esserci più richieste Web che devono eseguire le stesse query o aggiornamenti. Quindi verrà creata la classe ProductService per impedire la duplicazione del codice.

In breve, TDD guida il codice da scrivere. Il design si verifica quando si effettuano le scelte di implementazione, quindi si effettua il refactoring del codice in classi per eliminare le dipendenze di duplicazione e controllo. Man mano che aggiungi codice, dovrai creare nuove classi per mantenere il codice SOLID. Ma non è necessario decidere prima del tempo che occorrerà una classe Product e una classe ProductService. Potresti scoprire che la vita è perfetta con solo una classe di prodotto.

    
risposta data 29.05.2013 - 23:26
fonte
3

Gli altri potrebbero non essere d'accordo, ma per me molte delle più recenti metodologie si basano sul presupposto che lo sviluppatore farà la maggior parte di ciò che le vecchie metodologie spiegano solo per abitudine o orgoglio personale, che lo sviluppatore di solito fa qualcosa che è abbastanza ovvio per loro, e il lavoro è incapsulato in un linguaggio pulito o in parti più pulite di un linguaggio un po 'disordinato, così puoi fare tutto il lavoro di prova.

Alcuni esempi in cui mi sono imbattuto in questo in passato:

  • Prendi un gruppo di appaltatori di spec-work e dì loro che la loro squadra è Agile e prova prima. Spesso non hanno altra abitudine al lavoro spec e non hanno alcuna preoccupazione sulla qualità del lavoro fino a quando dura abbastanza a lungo per completare il progetto.

  • Prova e fai prima qualcosa di nuovo, spendi molto del tuo tempo prova quando trovi vari approcci e le interfacce sono schifose.

  • Codifica qualcosa di basso livello e ricevi uno schiaffo per mancanza di copertura, o scrivi un sacco di test che non hanno molto valore perché tu non può prendere in giro i comportamenti sottostanti a cui sei legato.

  • Qualsiasi situazione in cui non si dispone abbastanza della meccanica sottostante in anticipo per aggiungere una funzionalità testabile senza scrivere prima un mucchio di bit non testabili sottostanti, come il sottosistema del disco o un'interfaccia di comunicazione a livello di tcpip.

Se stai facendo TDD e sta funzionando per te, fa bene a te, ma ci sono molte cose (interi posti di lavoro, o fasi di un progetto) là fuori dove questo semplicemente non aggiunge valore.

Il tuo esempio sembra non avere ancora un design, quindi o devi avere una conversazione con l'architettura, o stai facendo un prototipo. Devi prima passare un po 'di quello, a mio parere.

    
risposta data 29.05.2013 - 22:09
fonte
1

Sono convinto che TDD sia un approccio molto valido per il design dettagliato del sistema, ovvero le API e il modello a oggetti. Tuttavia, per arrivare al punto in un progetto in cui si inizierebbe a utilizzare TDD, è necessario avere una visione d'insieme del design già modellato in qualche modo ed è necessario avere una visione d'insieme dell'architettura già modellata in qualche modo. @ user414076 parafrasa Robert Martin avendo in mente un'idea di design, ma non essendo sposato con esso. Esattamente. Conclusione: il TDD non è l'unica attività di progettazione in corso, ma è il modo in cui i dettagli del design si concretizzano. Il TDD deve essere preceduto da altre attività di progettazione e rientrare in un approccio generale (come ad esempio Agile) che affronta il modo in cui il design generale viene creato ed evoluto.

FYI - due libri che consiglio sull'argomento che forniscono esempi tangibili e realistici:

Crescere il software orientato agli oggetti, guidato dai test - spiega e fornisce un esempio completo di progetto. Questo è un libro su design, non testing . Il test viene utilizzato come mezzo per specificare il comportamento previsto durante le attività di progettazione.

Sviluppo guidato dai test Una guida pratica - una passeggiata lenta e dettagliata attraverso lo sviluppo di un completo , anche se piccola, app.

    
risposta data 05.06.2013 - 22:54
fonte
0

TTD guida la scoperta del design per fallimento del test, non con esito positivo, quindi puoi testare le incognite e ripetere test iterativamente man mano che le incognite vengono esposte, portando a un completo imbroglio dei test unitari - una cosa molto bella da avere per la manutenzione continua e una cosa molto difficile provare ad aggiornare dopo che il codice è stato scritto / rilasciato.

Ad esempio, un requisito può essere che l'input può essere in diversi formati, non tutti sono ancora noti. Usando TDD dovresti prima scrivere un test che verifica che l'output appropriato sia fornito dato qualsiasi formato di input. Ovviamente questo test fallirà, quindi si scrive codice per gestire i formati conosciuti e ripetere il test. Poiché i formati sconosciuti vengono esposti attraverso la raccolta dei requisiti, i nuovi test vengono scritti prima il codice viene scritto, anche questi non dovrebbero riuscire. Quindi viene scritto un nuovo codice per supportare i nuovi formati e i test tutti vengono rieseguiti riducendo le possibilità di regressione.

È anche utile pensare al fallimento dell'unità come codice "incompiuto" anziché codice "rotto". TDD consente unità non finite (guasti previsti), ma riduce il verificarsi di unità rotte (guasti imprevisti).

    
risposta data 29.05.2013 - 22:47
fonte
0

Nella domanda è indicato:

... many books pull a magic rabbit out of a hat and just dive into "Testing the ProductService", but they don't explain how they came to the conclusion that there is a ProductService in the first place.

Sono arrivati a questa conclusione pensando a come avrebbero provato questo prodotto. "Che tipo di prodotto fa questo?" "Bene, potremmo creare un servizio". "Ok, scriviamo un test per un servizio del genere"

    
risposta data 30.05.2013 - 01:12
fonte
0

Una funzionalità può avere molti design e TDD non ti dirà quale sia la migliore. Anche se i test ti aiuteranno a costruire un codice più modulare, può anche portarti a costruire moduli che soddisfino i requisiti dei test e non la realtà di produzione. Quindi devi capire dove stai andando e come le cose dovrebbero adattarsi all'intero quadro. In caso contrario, ci sono requisiti funzionali e non funzionali, non dimenticare l'ultimo.

Riguardo al design, mi riferisco ai libri di Robert C. Martin (Agile Development) ma anche a Patterns of Enterprise Application Architecture e Domain Driver Design di Martin Fowler. L'ultimo in particolare è molto sistematico nell'estrarre Entità e Relazioni dai requisiti.

Quindi, quando ottieni una buona sensazione delle opzioni disponibili su come gestire quelle entità, puoi nutrirti con l'approccio TDD.

    
risposta data 04.06.2013 - 07:50
fonte
0

Isn't TDD intended to make me discover my design in the first place?

No.

Come puoi testare qualcosa che non hai progettato prima?

To illustrate, imagine these 3 requirements:

  • A catalog needs to have a list of products
  • The catalog should remember which products a user viewed
  • Users should be able to search for a product

Questi non sono requisiti, queste sono definizioni di dati. Non so quale sia il business del tuo software, ma non è probabile che gli analisti parlino in questo modo.

Devi sapere quali sono gli invarianti del tuo sistema.

Un requisito sarebbe qualcosa di simile:

  • Un cliente può ordinare un certo quantitativo di prodotto, se c'è abbastanza di questo prodotto in magazzino.

Quindi, se questo è l'unico requisito, potresti avere una classe come:

public class Product {

  private int quantity;

  public Product(int initialQuantity) {
    this.quantity = initialQuantity;
  }

  public void order(int quantity) {
    // To be implemented.
  }

}

Quindi, usando TDD, scriveresti un test case prima di implementare il metodo order ().

public void ProductTest() {

    public void testCorrectOrder() {

        Product p = new Product(10);
        p.order(3);
        p.order(4);

    }

    @Expect(ProductOutOfStockException)
    public void testIncorrectOrder() {

        Product p = new Product(10);
        p.order(7);
        p.order(4);

    }

}

Quindi il secondo test fallirà, quindi puoi implementare il metodo order () nel modo che preferisci.

    
risposta data 04.06.2013 - 12:21
fonte
0

Sei completamente corretto TDD si tradurrà in una buona implementazione di un dato progetto. Non aiuterà il tuo processo di progettazione.

    
risposta data 06.06.2013 - 03:59
fonte
-3

TDD aiuta molto, tuttavia c'è una parte importante nello sviluppo del software. Lo sviluppatore dovrebbe ascoltare il codice che viene scritto. Il refactoring è la terza parte del ciclo TDD. Questo è il passaggio principale in cui lo sviluppatore dovrebbe concentrarsi e riflettere prima di passare al prossimo test rosso. C'è qualche duplicazione? I principi SOLID sono applicati? Che dire di alta coesione e basso accoppiamento? E i nomi? Guarda più da vicino il codice che emerge dai test e vedi se c'è qualcosa che deve essere cambiato, ridisegnato. Metti in discussione il codice e il codice ti dirà come vuoi che venga progettato. Di solito scrivo serie di test multipli, esamino quell'elenco e creo il primo design semplice, non è necessario che sia "definitivo", di solito non lo è, perché è cambiato quando si aggiungono nuovi test. Ecco dove arriva il design.

    
risposta data 04.06.2013 - 21:56
fonte

Leggi altre domande sui tag